Hi,
Here's an old patchset proposed a while ago by Boris Baykov to add 4byte support to flashrom. I have been using it to flash N25Q256 modules from a raspberry pi quite successfully. Could we restart a review cycle to see what needs fixing ?
A port of the complete patchset on 0.9.9 is available here :
https://github.com/legoater/flashrom
Thanks,
C.
Boris Baykov (6): 4BA: Basic support for 4-bytes addressing mode extensions 4BA: Flashrom integration for the 4-bytes addressing extensions 4BA: Winbond W25Q256.V chip (32MB) declaration, 4-bytes addr mode 4BA: Support for 4-bytes addressing via Extended Address Register 4BA: Support for new direct-4BA instructions + W25Q256.V update 4BA: Progress visualization for long read, writes and erases
Makefile | 2 +- chipdrivers.h | 22 ++ cli_output.c | 3 +- flash.h | 21 ++ flashchips.c | 48 +++ flashrom.c | 61 ++++ serprog.c | 5 +- spi.c | 5 +- spi25.c | 37 ++- spi4ba.c | 920 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ spi4ba.h | 114 ++++++++ 11 files changed, 1231 insertions(+), 7 deletions(-) create mode 100644 spi4ba.c create mode 100644 spi4ba.h
From: Boris Baykov dev@borisbaykov.com
If flash chip is switched to 4-bytes addressing mode then all read/erase/program instructions will be switched from 3-bytes mode to 4-bytes mode. Then well known instructions like 03h (Read), 02h (Program) and 20h,52h,D8h (Erase) will become one byte longer and accept 4-bytes address instead of 3-bytes.
This patch provides support for well known instructions in 4-bytes addressing mode. Also here is the code to enter 4-bytes addressing mode by execute the instruction B7h (Enter 4-bytes mode).
Patched files ------------- chipdrivers.h + added functions declarations for spi4ba.c
flash.h + feature definitions added
Makefile + added spi4ba.c
Added files ----------- spi4ba.h + definitions for 4-bytes addressing JEDEC commands + functions declarations from spi4ba.c (same as in chipdrivers.h, just to see)
spi4ba.c + functions for enter 4-bytes addressing mode + functions for read/write/erase in 4-bytes addressing mode
Signed-off-by: Boris Baykov dev@borisbaykov.com, Russia, Jan 2014 [clg: ported from https://www.flashrom.org/pipermail/flashrom/2015-January/013199.html ] Signed-off-by: Cédric Le Goater clg@kaod.org --- Makefile | 2 +- chipdrivers.h | 10 ++ flash.h | 3 + spi4ba.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ spi4ba.h | 54 ++++++++++ 5 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 spi4ba.c create mode 100644 spi4ba.h
diff --git a/Makefile b/Makefile index 4ebde1efbe3f..d7713986008e 100644 --- a/Makefile +++ b/Makefile @@ -514,7 +514,7 @@ endif CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \ sst28sf040.o 82802ab.o \ sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o spi25_statusreg.o \ - opaque.o sfdp.o en29lv640b.o at45db.o + spi4ba.o opaque.o sfdp.o en29lv640b.o at45db.o
############################################################################### # Library code. diff --git a/chipdrivers.h b/chipdrivers.h index c85eac96cc31..dccd1ff92ec1 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -195,4 +195,14 @@ int erase_sector_stm50(struct flashctx *flash, unsigned int block, unsigned int int probe_en29lv640b(struct flashctx *flash); int write_en29lv640b(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+/* spi4ba.c */ +int spi_enter_4ba_b7(struct flashctx *flash); +int spi_enter_4ba_b7_we(struct flashctx *flash); +int spi_byte_program_4ba(struct flashctx *flash, unsigned int addr, uint8_t databyte); +int spi_nbyte_program_4ba(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); +int spi_nbyte_read_4ba(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); +int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); + #endif /* !__CHIPDRIVERS_H__ */ diff --git a/flash.h b/flash.h index da049d178439..7bd4b1376d40 100644 --- a/flash.h +++ b/flash.h @@ -123,6 +123,9 @@ enum write_granularity { #define FEATURE_WRSR_EITHER (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN) #define FEATURE_OTP (1 << 8) #define FEATURE_QPI (1 << 9) +/* Feature bits used for 4-bytes addressing mode */ +#define FEATURE_4BA_SUPPORT (1 << 10) +#define FEATURE_4BA_ONLY (1 << 11)
enum test_state { OK = 0, diff --git a/spi4ba.c b/spi4ba.c new file mode 100644 index 000000000000..72df874275c9 --- /dev/null +++ b/spi4ba.c @@ -0,0 +1,327 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2014 Boris Baykov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * SPI chip driver functions for 4-bytes addressing + */ + +#include <string.h> +#include "flash.h" +#include "chipdrivers.h" +#include "spi.h" +#include "programmer.h" +#include "spi4ba.h" + +/* #define MSG_TRACE_4BA_FUNCS 1 */ + +#ifdef MSG_TRACE_4BA_FUNCS +#define msg_trace(...) print(MSG_DEBUG, __VA_ARGS__) +#else +#define msg_trace(...) +#endif + +/* Enter 4-bytes addressing mode (without sending WREN before) */ +int spi_enter_4ba_b7(struct flashctx *flash) +{ + 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); +} + +/* Enter 4-bytes addressing mode with sending WREN before */ +int spi_enter_4ba_b7_we(struct flashctx *flash) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_ENTER_4_BYTE_ADDR_MODE_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_ENTER_4_BYTE_ADDR_MODE }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s\n", __func__); + + /* Switch to 4-bytes addressing mode */ + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution\n", __func__); + } + return result; +} + +/* Program one flash byte from 4-bytes addressing mode */ +int spi_byte_program_4ba(struct flashctx *flash, unsigned int addr, + uint8_t databyte) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE + 1, + .writearr = (const unsigned char[]){ + JEDEC_BYTE_PROGRAM, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff), + databyte + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X)\n", __func__, addr); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + } + return result; +} + +/* Program flash bytes from 4-bytes addressing mode */ +int spi_nbyte_program_4ba(struct flashctx *flash, unsigned int addr, + const uint8_t *bytes, unsigned int len) +{ + int result; + unsigned char cmd[(JEDEC_BYTE_PROGRAM_OUTSIZE + 1) - 1 + 256] = { + JEDEC_BYTE_PROGRAM, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr >> 0) & 0xff + }; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = (JEDEC_BYTE_PROGRAM_OUTSIZE + 1) - 1 + len, + .writearr = cmd, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1); + + if (!len) { + msg_cerr("%s called for zero-length write\n", __func__); + return 1; + } + if (len > 256) { + msg_cerr("%s called for too long a write\n", __func__); + return 1; + } + + memcpy(&cmd[(JEDEC_BYTE_PROGRAM_OUTSIZE + 1) - 1], bytes, len); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + } + return result; +} + +/* Read flash bytes from 4-bytes addressing mode */ +int spi_nbyte_read_4ba(struct flashctx *flash, unsigned int addr, + uint8_t *bytes, unsigned int len) +{ + const unsigned char cmd[JEDEC_READ_OUTSIZE + 1] = { + JEDEC_READ, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr >> 0) & 0xff + }; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1); + + /* Send Read */ + return spi_send_command(flash, sizeof(cmd), len, cmd, bytes); +} + +/* Erases 4 KB of flash from 4-bytes addressing mode */ +int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_SE_OUTSIZE + 1, + .writearr = (const unsigned char[]){ + JEDEC_SE, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 15-800 ms, so wait in 10 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(10 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} + +/* Erases 32 KB of flash from 4-bytes addressing mode */ +int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BE_52_OUTSIZE + 1, + .writearr = (const unsigned char[]){ + JEDEC_BE_52, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 100-4000 ms, so wait in 100 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(100 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} + +/* Erases 64 KB of flash from 4-bytes addressing mode */ +int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BE_D8_OUTSIZE + 1, + .writearr = (const unsigned char[]){ + JEDEC_BE_D8, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 100-4000 ms, so wait in 100 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(100 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} diff --git a/spi4ba.h b/spi4ba.h new file mode 100644 index 000000000000..15feecb40802 --- /dev/null +++ b/spi4ba.h @@ -0,0 +1,54 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2014 Boris Baykov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + * JEDEC flash chips instructions for 4-bytes addressing + * SPI chip driver functions for 4-bytes addressing + */ + +#ifndef __SPI_4BA_H__ +#define __SPI_4BA_H__ 1 + +/* Enter 4-byte Address Mode */ +#define JEDEC_ENTER_4_BYTE_ADDR_MODE 0xB7 +#define JEDEC_ENTER_4_BYTE_ADDR_MODE_OUTSIZE 0x01 +#define JEDEC_ENTER_4_BYTE_ADDR_MODE_INSIZE 0x00 + +/* Exit 4-byte Address Mode */ +#define JEDEC_EXIT_4_BYTE_ADDR_MODE 0xE9 +#define JEDEC_EXIT_4_BYTE_ADDR_MODE_OUTSIZE 0x01 +#define JEDEC_EXIT_4_BYTE_ADDR_MODE_INSIZE 0x00 + +/* enter 4-bytes addressing mode */ +int spi_enter_4ba_b7(struct flashctx *flash); +int spi_enter_4ba_b7_we(struct flashctx *flash); + +/* read/write flash bytes in 4-bytes addressing mode */ +int spi_byte_program_4ba(struct flashctx *flash, unsigned int addr, uint8_t databyte); +int spi_nbyte_program_4ba(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); +int spi_nbyte_read_4ba(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); + +/* erase flash bytes in 4-bytes addressing mode */ +int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); + + +#endif /* __SPI_4BA_H__ */
From: Boris Baykov dev@borisbaykov.com
This patch integrates code of the previous patch into Flashrom's code. All the integrations is around 3 functions spi_nbyte_read, spi_nbyte_program and spi_byte_program. After this patch then are not static and can be called by their pointers saved in flashchips array. Also I added to flashrom.c some code to switch a chip to 4-bytes addressing mode. And one error message is corrected in spi.c because it's not suitable for 32-bit addresses.
Patched files ------------- flash.h + added set of 4-bytes address functions to flashchip structure definition
flashrom.c + added switch to 4-bytes addressing more for chips which support it
serprog.c + added 4-bytes addressing spi_nbyte_read call to serprog_spi_read
spi.c + fixed flash chip size check in spi_chip_read
spi25.c + added 4-bytes addressing spi_nbyte_read call to spi_read_chunked + added 4-bytes addressing spi_nbyte_program call to spi_write_chunked + added 4-bytes addressing spi_byte_program call to spi_chip_write_1
Signed-off-by: Boris Baykov dev@borisbaykov.com, Russia, Jan 2014 [clg: ported from https://www.flashrom.org/pipermail/flashrom/2015-January/013205.html ] Signed-off-by: Cédric Le Goater clg@kaod.org --- flash.h | 8 ++++++++ flashrom.c | 23 +++++++++++++++++++++++ serprog.c | 5 ++++- spi.c | 5 ++++- spi25.c | 15 ++++++++++++--- 5 files changed, 51 insertions(+), 5 deletions(-)
diff --git a/flash.h b/flash.h index 7bd4b1376d40..38b30f8d8a51 100644 --- a/flash.h +++ b/flash.h @@ -170,6 +170,14 @@ struct flashchip { unsigned int page_size; int feature_bits;
+ /* set of function pointers to use in 4-bytes addressing mode */ + struct four_bytes_addr_funcs_set { + int (*enter_4ba) (struct flashctx *flash); + int (*read_nbyte) (struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); + int (*program_byte) (struct flashctx *flash, unsigned int addr, const uint8_t databyte); + int (*program_nbyte) (struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); + } four_bytes_addr_funcs; + /* Indicate how well flashrom supports different operations of this flash chip. */ struct tested { enum test_state probe; diff --git a/flashrom.c b/flashrom.c index 25e53f20c059..c84843fa7c4a 100644 --- a/flashrom.c +++ b/flashrom.c @@ -2001,6 +2001,29 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, if (flash->chip->unlock) flash->chip->unlock(flash);
+ /* Switching to 4-Bytes Addressing mode if flash chip supports it */ + if(flash->chip->feature_bits & FEATURE_4BA_SUPPORT) { + /* Do not switch if chip is already in 4-bytes addressing mode */ + if (flash->chip->feature_bits & FEATURE_4BA_ONLY) { + msg_cdbg("Flash chip is already in 4-bytes addressing mode.\n"); + } + /* Go to 4-Bytes Addressing mode */ + else { + if (!flash->chip->four_bytes_addr_funcs.enter_4ba) { + msg_cerr("No function for Enter 4-bytes addressing mode for this flash chip.\n" + "Please report to flashrom@flashrom.org\n"); + return 1; + } + + if(flash->chip->four_bytes_addr_funcs.enter_4ba(flash)) { + msg_cerr("Switching to 4-bytes addressing mode failed!\n"); + return 1; + } + + msg_cdbg("Switched to 4-bytes addressing mode.\n"); + } + } + if (read_it) { return read_flash_to_file(flash, filename); } diff --git a/serprog.c b/serprog.c index 98aac8314ff0..c9d98bf099fc 100644 --- a/serprog.c +++ b/serprog.c @@ -945,7 +945,10 @@ static int serprog_spi_read(struct flashctx *flash, uint8_t *buf, for (i = 0; i < len; i += cur_len) { int ret; cur_len = min(max_read, (len - i)); - ret = spi_nbyte_read(flash, start + i, buf + i, cur_len); + ret = (flash->chip->feature_bits & FEATURE_4BA_SUPPORT) == 0 + ? spi_nbyte_read(flash, start + i, buf + i, cur_len) + : flash->chip->four_bytes_addr_funcs.read_nbyte(flash, + start + i, buf + i, cur_len); if (ret) return ret; } diff --git a/spi.c b/spi.c index 894f73f60f43..0a4a6184c06e 100644 --- a/spi.c +++ b/spi.c @@ -110,7 +110,10 @@ int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, * means 0xffffff, the highest unsigned 24bit number. */ addrbase = spi_get_valid_read_addr(flash); - if (addrbase + flash->chip->total_size * 1024 > (1 << 24)) { + /* Show flash chip size warning if flash chip doesn't support + 4-Bytes Addressing mode and last address excedes 24 bits */ + if (!(flash->chip->feature_bits & FEATURE_4BA_SUPPORT) && + addrbase + flash->chip->total_size * 1024 > (1 << 24)) { msg_perr("Flash chip size exceeds the allowed access window. "); msg_perr("Read will probably fail.\n"); /* Try to get the best alignment subject to constraints. */ diff --git a/spi25.c b/spi25.c index af4b6db0ea09..b38c7444e873 100644 --- a/spi25.c +++ b/spi25.c @@ -28,6 +28,7 @@ #include "chipdrivers.h" #include "programmer.h" #include "spi.h" +#include "spi4ba.h"
static int spi_rdid(struct flashctx *flash, unsigned char *readarr, int bytes) { @@ -966,7 +967,10 @@ int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, lenhere = min(start + len, (i + 1) * page_size) - starthere; for (j = 0; j < lenhere; j += chunksize) { toread = min(chunksize, lenhere - j); - rc = spi_nbyte_read(flash, starthere + j, buf + starthere - start + j, toread); + rc = (flash->chip->feature_bits & FEATURE_4BA_SUPPORT) == 0 + ? spi_nbyte_read(flash, starthere + j, buf + starthere - start + j, toread) + : flash->chip->four_bytes_addr_funcs.read_nbyte(flash, starthere + j, + buf + starthere - start + j, toread); if (rc) break; } @@ -1011,7 +1015,10 @@ int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int s lenhere = min(start + len, (i + 1) * page_size) - starthere; for (j = 0; j < lenhere; j += chunksize) { towrite = min(chunksize, lenhere - j); - rc = spi_nbyte_program(flash, starthere + j, buf + starthere - start + j, towrite); + rc = (flash->chip->feature_bits & FEATURE_4BA_SUPPORT) == 0 + ? spi_nbyte_program(flash, starthere + j, buf + starthere - start + j, towrite) + : flash->chip->four_bytes_addr_funcs.program_nbyte(flash, starthere + j, + buf + starthere - start + j, towrite); if (rc) break; while (spi_read_status_register(flash) & SPI_SR_WIP) @@ -1037,7 +1044,9 @@ int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int st int result = 0;
for (i = start; i < start + len; i++) { - result = spi_byte_program(flash, i, buf[i - start]); + result = (flash->chip->feature_bits & FEATURE_4BA_SUPPORT) == 0 + ? spi_byte_program(flash, i, buf[i - start]) + : flash->chip->four_bytes_addr_funcs.program_byte(flash, i, buf[i - start]); if (result) return 1; while (spi_read_status_register(flash) & SPI_SR_WIP)
From: Boris Baykov dev@borisbaykov.com
Here is the definition of new W25Q256xV chip with new functions pointers for 4-bytes addressing reads and writes. Erase functions pointers are changed in their old places. New feature flags for 4-bytes mode added.
Patched files ------------- flashchips.c + added definition for Winbond W25Q256BV/W25Q256FV chips
Signed-off-by: Boris Baykov dev@borisbaykov.com, Russia, Jan 2014 [clg: ported from https://www.flashrom.org/pipermail/flashrom/2015-January/013201.html ] Signed-off-by: Cédric Le Goater clg@kaod.org --- flashchips.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
diff --git a/flashchips.c b/flashchips.c index 40b6b8e2dbe9..21055e64cd22 100644 --- a/flashchips.c +++ b/flashchips.c @@ -14588,6 +14588,54 @@ const struct flashchip flashchips[] = {
{ .vendor = "Winbond", + .name = "W25Q256.V", + .bustype = BUS_SPI, + .manufacture_id = WINBOND_NEX_ID, + .model_id = WINBOND_NEX_W25Q256_V, + .total_size = 32768, + .page_size = 256, + /* supports SFDP */ + /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ + /* FOUR_BYTE_ADDR: supports 4-bytes addressing mode */ + .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT, + .four_bytes_addr_funcs = + { + .enter_4ba = spi_enter_4ba_b7_we, /* enter 4-bytes addressing mode by CMD B7 + WREN */ + .read_nbyte = spi_nbyte_read_4ba, /* read from 4-bytes addressing mode */ + .program_byte = spi_byte_program_4ba, /* write from 4-bytes addressing mode */ + .program_nbyte = spi_nbyte_program_4ba /* write from 4-bytes addressing mode */ + }, + .tested = TEST_OK_PREW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 8192} }, + .block_erase = spi_block_erase_20_4ba, /* erases 4k from 4-bytes addressing mode */ + }, { + .eraseblocks = { {32 * 1024, 1024} }, + .block_erase = spi_block_erase_52_4ba, /* erases 32k from 4-bytes addressing mode */ + }, { + .eraseblocks = { {64 * 1024, 512} }, + .block_erase = spi_block_erase_d8_4ba, /* erases 64k from 4-bytes addressing mode */ + }, { + .eraseblocks = { {32 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_60, + }, { + .eraseblocks = { {32 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_c7, + } + }, + .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */ + .unlock = spi_disable_blockprotect, + .write = spi_chip_write_256, + .read = spi_chip_read, + .voltage = {2700, 3600}, + }, + + { + .vendor = "Winbond", .name = "W25Q20.W", .bustype = BUS_SPI, .manufacture_id = WINBOND_NEX_ID,
From: Boris Baykov dev@borisbaykov.com
On some flash chips data with addresses more than 24-bit field can address may be accessed by using Extended Address Register. The register has 1-byte size and stores high byte of 32-bit address. Then flash can be read from 3-bytes addressing mode with writing high byte of address to this Register. By using this way we have access to full memory of a chip. Some chips may support this method only.
This patch provides code use Extended Address Register.
Patched files ------------- chipdrivers.h + added functions declarations for spi4ba.c
flash.h + feature definitions added
flashrom.c + modified switch to 4-bytes addressing to support extended address register
spi4ba.h + definitions for 4-bytes addressing JEDEC commands + functions declarations from spi4ba.c (same as in chipdrivers.h, just to see)
spi4ba.c + functions for write Extended Address Register + functions for read/write/erase with Extended Address Register
Signed-off-by: Boris Baykov dev@borisbaykov.com, Russia, Jan 2014 [clg: ported from https://www.flashrom.org/pipermail/flashrom/2015-January/013200.html ] Signed-off-by: Cédric Le Goater clg@kaod.org --- chipdrivers.h | 6 ++ flash.h | 1 + flashrom.c | 4 + spi4ba.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ spi4ba.h | 20 ++++ 5 files changed, 359 insertions(+)
diff --git a/chipdrivers.h b/chipdrivers.h index dccd1ff92ec1..a3da86570c42 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -204,5 +204,11 @@ int spi_nbyte_read_4ba(struct flashctx *flash, unsigned int addr, uint8_t *bytes int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_byte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t databyte); +int spi_nbyte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); +int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); +int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
#endif /* !__CHIPDRIVERS_H__ */ diff --git a/flash.h b/flash.h index 38b30f8d8a51..cc374b798f07 100644 --- a/flash.h +++ b/flash.h @@ -126,6 +126,7 @@ enum write_granularity { /* Feature bits used for 4-bytes addressing mode */ #define FEATURE_4BA_SUPPORT (1 << 10) #define FEATURE_4BA_ONLY (1 << 11) +#define FEATURE_4BA_EXTENDED_ADDR_REG (1 << 12)
enum test_state { OK = 0, diff --git a/flashrom.c b/flashrom.c index c84843fa7c4a..79ddf76ad3d6 100644 --- a/flashrom.c +++ b/flashrom.c @@ -2007,6 +2007,10 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, if (flash->chip->feature_bits & FEATURE_4BA_ONLY) { msg_cdbg("Flash chip is already in 4-bytes addressing mode.\n"); } + /* Do not switch to 4-Bytes Addressing mode if using Extended Address Register */ + else if(flash->chip->feature_bits & FEATURE_4BA_EXTENDED_ADDR_REG) { + msg_cdbg("Using 4-bytes addressing with extended address register.\n"); + } /* Go to 4-Bytes Addressing mode */ else { if (!flash->chip->four_bytes_addr_funcs.enter_4ba) { diff --git a/spi4ba.c b/spi4ba.c index 72df874275c9..db31cd2064ca 100644 --- a/spi4ba.c +++ b/spi4ba.c @@ -325,3 +325,331 @@ int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, /* FIXME: Check the status register for errors. */ return 0; } + +/* Write Extended Address Register value */ +int spi_write_extended_address_register(struct flashctx *flash, uint8_t regdata) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_WRITE_EXT_ADDR_REG_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_WRITE_EXT_ADDR_REG, + regdata + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (%02X)\n", __func__, regdata); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution\n", __func__); + return result; + } + return 0; +} + +/* Assign required value of Extended Address Register. This function + keeps last value of the register and writes the register if the + value has to be changed only. */ +int set_extended_address_register(struct flashctx *flash, uint8_t data) +{ + static uint8_t ext_addr_reg_state; /* memory for last register state */ + static int ext_addr_reg_state_valid = 0; + int result; + + if (ext_addr_reg_state_valid == 0 || data != ext_addr_reg_state) { + result = spi_write_extended_address_register(flash, data); + if (result) { + ext_addr_reg_state_valid = 0; + return result; + } + ext_addr_reg_state = data; + ext_addr_reg_state_valid = 1; + } + return 0; +} + +/* Program one flash byte using Extended Address Register + from 3-bytes addressing mode */ +int spi_byte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, + uint8_t databyte) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_BYTE_PROGRAM, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff), + databyte + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X)\n", __func__, addr); + + result = set_extended_address_register(flash, (addr >> 24) & 0xff); + if (result) + return result; + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + } + return result; +} + +/* Program flash bytes using Extended Address Register + from 3-bytes addressing mode */ +int spi_nbyte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, + const uint8_t *bytes, unsigned int len) +{ + int result; + unsigned char cmd[JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + 256] = { + JEDEC_BYTE_PROGRAM, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr >> 0) & 0xff + }; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + len, + .writearr = cmd, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1); + + if (!len) { + msg_cerr("%s called for zero-length write\n", __func__); + return 1; + } + if (len > 256) { + msg_cerr("%s called for too long a write\n", __func__); + return 1; + } + + memcpy(&cmd[JEDEC_BYTE_PROGRAM_OUTSIZE - 1], bytes, len); + + result = set_extended_address_register(flash, (addr >> 24) & 0xff); + if (result) + return result; + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + } + return result; +} + +/* Read flash bytes using Extended Address Register + from 3-bytes addressing mode */ +int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr, + uint8_t *bytes, unsigned int len) +{ + int result; + const unsigned char cmd[JEDEC_READ_OUTSIZE] = { + JEDEC_READ, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr >> 0) & 0xff + }; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1); + + result = set_extended_address_register(flash, (addr >> 24) & 0xff); + if (result) + return result; + + /* Send Read */ + return spi_send_command(flash, sizeof(cmd), len, cmd, bytes); +} + +/* Erases 4 KB of flash using Extended Address Register + from 3-bytes addressing mode */ +int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_SE_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_SE, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = set_extended_address_register(flash, (addr >> 24) & 0xff); + if (result) + return result; + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 15-800 ms, so wait in 10 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(10 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} + +/* Erases 32 KB of flash using Extended Address Register + from 3-bytes addressing mode */ +int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BE_52_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_BE_52, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = set_extended_address_register(flash, (addr >> 24) & 0xff); + if (result) + return result; + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 100-4000 ms, so wait in 100 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(100 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} + +/* Erases 64 KB of flash using Extended Address Register + from 3-bytes addressing mode */ +int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BE_D8_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_BE_D8, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = set_extended_address_register(flash, (addr >> 24) & 0xff); + if (result) + return result; + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 100-4000 ms, so wait in 100 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(100 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} diff --git a/spi4ba.h b/spi4ba.h index 15feecb40802..f2e7bc54c669 100644 --- a/spi4ba.h +++ b/spi4ba.h @@ -36,6 +36,16 @@ #define JEDEC_EXIT_4_BYTE_ADDR_MODE_OUTSIZE 0x01 #define JEDEC_EXIT_4_BYTE_ADDR_MODE_INSIZE 0x00
+/* Write Extended Address Register */ +#define JEDEC_WRITE_EXT_ADDR_REG 0xC5 +#define JEDEC_WRITE_EXT_ADDR_REG_OUTSIZE 0x02 +#define JEDEC_WRITE_EXT_ADDR_REG_INSIZE 0x00 + +/* Read Extended Address Register */ +#define JEDEC_READ_EXT_ADDR_REG 0xC8 +#define JEDEC_READ_EXT_ADDR_REG_OUTSIZE 0x01 +#define JEDEC_READ_EXT_ADDR_REG_INSIZE 0x01 + /* enter 4-bytes addressing mode */ int spi_enter_4ba_b7(struct flashctx *flash); int spi_enter_4ba_b7_we(struct flashctx *flash); @@ -50,5 +60,15 @@ int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr, unsigned i int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+/* read/write flash bytes from 3-bytes addressing mode using extended address register */ +int spi_byte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t databyte); +int spi_nbyte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); +int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); + +/* erase flash bytes from 3-bytes addressing mode using extended address register */ +int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +
#endif /* __SPI_4BA_H__ */
From: Boris Baykov dev@borisbaykov.com
Large flash chips usually support special instructions to work with 4-bytes address directly from 3-bytes addressing mode and without do switching to 4-bytes mode. There are 13h (4BA Read), 12h (4BA Program) and 21h,5Ch,DCh (4BA Erase), correspondingly. However not all these instructions are supported by all large flash chips. Some chips support 13h only, some 13h,12h,21h and DCh, but not 5Ch. This depends on the manufacturer of the chip.
This patch provides code to use direct 4-bytes addressing instructions.
This code should work but it tested partially only. My W25Q256FV has support for 4BA_Read (13h), but doesn't have support 4BA_Program (12h) and 4BA_Erase instructions. So, direct 4BA program and erase should be tested after.
Patched files ------------- chipdrivers.h + added functions declarations for spi4ba.c
flash.h + feature definitions added
flashchips.c + modified definition of Winbond W25Q256BV/W25Q256FV chips
flashrom.c + modified switch to 4-bytes addressing for direct-4BA instructions
spi4ba.h + definitions for 4-bytes addressing JEDEC commands + functions declarations from spi4ba.c (same as in chipdrivers.h, just to see)
spi4ba.c + functions for read/write/erase directly with 4-bytes address (from any mode)
Signed-off-by: Boris Baykov dev@borisbaykov.com, Russia, Jan 2014 [clg: ported from https://www.flashrom.org/pipermail/flashrom/2015-January/013198.html ] Signed-off-by: Cédric Le Goater clg@kaod.org --- chipdrivers.h | 6 ++ flash.h | 4 + flashchips.c | 4 +- flashrom.c | 15 +++- spi4ba.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ spi4ba.h | 40 +++++++++ 6 files changed, 330 insertions(+), 4 deletions(-)
diff --git a/chipdrivers.h b/chipdrivers.h index a3da86570c42..20529d5be4ce 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -210,5 +210,11 @@ int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t * int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t databyte); +int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); +int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); +int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +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);
#endif /* !__CHIPDRIVERS_H__ */ diff --git a/flash.h b/flash.h index cc374b798f07..7f79387a9251 100644 --- a/flash.h +++ b/flash.h @@ -127,6 +127,10 @@ enum write_granularity { #define FEATURE_4BA_SUPPORT (1 << 10) #define FEATURE_4BA_ONLY (1 << 11) #define FEATURE_4BA_EXTENDED_ADDR_REG (1 << 12) +#define FEATURE_4BA_DIRECT_READ (1 << 13) +#define FEATURE_4BA_DIRECT_WRITE (1 << 14) +#define FEATURE_4BA_ALL_ERASERS_DIRECT (1 << 15) +#define FEATURE_4BA_ALL_DIRECT (FEATURE_4BA_DIRECT_READ | FEATURE_4BA_DIRECT_WRITE | FEATURE_4BA_ALL_ERASERS_DIRECT)
enum test_state { OK = 0, diff --git a/flashchips.c b/flashchips.c index 21055e64cd22..7cd12fadb312 100644 --- a/flashchips.c +++ b/flashchips.c @@ -14597,11 +14597,11 @@ const struct flashchip flashchips[] = { /* supports SFDP */ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */ /* FOUR_BYTE_ADDR: supports 4-bytes addressing mode */ - .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT, + .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT | FEATURE_4BA_DIRECT_READ, .four_bytes_addr_funcs = { .enter_4ba = spi_enter_4ba_b7_we, /* enter 4-bytes addressing mode by CMD B7 + WREN */ - .read_nbyte = spi_nbyte_read_4ba, /* read from 4-bytes addressing mode */ + .read_nbyte = spi_nbyte_read_4ba_direct, /* read directly from any mode, no need to enter 4ba */ .program_byte = spi_byte_program_4ba, /* write from 4-bytes addressing mode */ .program_nbyte = spi_nbyte_program_4ba /* write from 4-bytes addressing mode */ }, diff --git a/flashrom.c b/flashrom.c index 79ddf76ad3d6..6a6f5b6f6af8 100644 --- a/flashrom.c +++ b/flashrom.c @@ -2011,8 +2011,14 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, else if(flash->chip->feature_bits & FEATURE_4BA_EXTENDED_ADDR_REG) { msg_cdbg("Using 4-bytes addressing with extended address register.\n"); } - /* Go to 4-Bytes Addressing mode */ - else { + /* Go to 4-Bytes Addressing mode if selected + operation requires 4-Bytes Addressing mode + (no need if functions are direct-4BA) */ + else if(((read_it || verify_it) + && (!(flash->chip->feature_bits & FEATURE_4BA_DIRECT_READ))) + || ((erase_it || write_it) + && ((flash->chip->feature_bits & FEATURE_4BA_ALL_DIRECT) != FEATURE_4BA_ALL_DIRECT))) { + if (!flash->chip->four_bytes_addr_funcs.enter_4ba) { msg_cerr("No function for Enter 4-bytes addressing mode for this flash chip.\n" "Please report to flashrom@flashrom.org\n"); @@ -2026,6 +2032,11 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it,
msg_cdbg("Switched to 4-bytes addressing mode.\n"); } + /* Do not switch to 4-Bytes Addressing mode if all instructions are direct-4BA + or if the flash chip is 4-Bytes Addressing Only and always in 4BA-mode */ + else { + msg_cdbg2("No need to switch to 4-bytes addressing mode.\n"); + } }
if (read_it) { diff --git a/spi4ba.c b/spi4ba.c index db31cd2064ca..6e1cc9b8d511 100644 --- a/spi4ba.c +++ b/spi4ba.c @@ -653,3 +653,268 @@ int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, /* FIXME: Check the status register for errors. */ return 0; } + +/* Program one flash byte with 4-bytes address from ANY mode (3-bytes or 4-bytes) + JEDEC_BYTE_PROGRAM_4BA (12h) instruction is new for 4-bytes addressing flash chips. + The presence of this instruction for an exact chip should be checked + by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */ +int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr, + uint8_t databyte) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BYTE_PROGRAM_4BA_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_BYTE_PROGRAM_4BA, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff), + databyte + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X)\n", __func__, addr); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + } + return result; +} + +/* Program flash bytes with 4-bytes address from ANY mode (3-bytes or 4-bytes) + JEDEC_BYTE_PROGRAM_4BA (12h) instruction is new for 4-bytes addressing flash chips. + The presence of this instruction for an exact chip should be checked + by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */ +int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr, + const uint8_t *bytes, unsigned int len) +{ + int result; + unsigned char cmd[JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1 + 256] = { + JEDEC_BYTE_PROGRAM_4BA, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr >> 0) & 0xff + }; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1 + len, + .writearr = cmd, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1); + + if (!len) { + msg_cerr("%s called for zero-length write\n", __func__); + return 1; + } + if (len > 256) { + msg_cerr("%s called for too long a write\n", __func__); + return 1; + } + + memcpy(&cmd[JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1], bytes, len); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + } + return result; +} + +/* Read flash bytes with 4-bytes address from ANY mode (3-bytes or 4-bytes) + JEDEC_READ_4BA (13h) instruction is new for 4-bytes addressing flash chips. + The presence of this instruction for an exact chip should be checked + by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */ +int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr, + uint8_t *bytes, unsigned int len) +{ + const unsigned char cmd[JEDEC_READ_4BA_OUTSIZE] = { + JEDEC_READ_4BA, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr >> 0) & 0xff + }; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1); + + /* Send Read */ + return spi_send_command(flash, sizeof(cmd), len, cmd, bytes); +} + +/* Erase 4 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes) + JEDEC_SE_4BA (21h) instruction is new for 4-bytes addressing flash chips. + The presence of this instruction for an exact chip should be checked + by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */ +int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_SE_4BA_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_SE_4BA, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 15-800 ms, so wait in 10 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(10 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} + +/* Erase 32 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes) + JEDEC_BE_5C_4BA (5Ch) instruction is new for 4-bytes addressing flash chips. + The presence of this instruction for an exact chip should be checked + by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */ +int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BE_5C_4BA_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_BE_5C_4BA, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 100-4000 ms, so wait in 100 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(100 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} + +/* Erase 64 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes) + JEDEC_BE_DC_4BA (DCh) instruction is new for 4-bytes addressing flash chips. + The presence of this instruction for an exact chip should be checked + by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */ +int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BE_DC_4BA_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_BE_DC_4BA, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1); + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 100-4000 ms, so wait in 100 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(100 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} diff --git a/spi4ba.h b/spi4ba.h index f2e7bc54c669..8e500d159143 100644 --- a/spi4ba.h +++ b/spi4ba.h @@ -46,6 +46,36 @@ #define JEDEC_READ_EXT_ADDR_REG_OUTSIZE 0x01 #define JEDEC_READ_EXT_ADDR_REG_INSIZE 0x01
+/* Read the memory with 4-byte address + From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */ +#define JEDEC_READ_4BA 0x13 +#define JEDEC_READ_4BA_OUTSIZE 0x05 +/* JEDEC_READ_4BA_INSIZE : any length */ + +/* Write memory byte with 4-byte address + From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */ +#define JEDEC_BYTE_PROGRAM_4BA 0x12 +#define JEDEC_BYTE_PROGRAM_4BA_OUTSIZE 0x06 +#define JEDEC_BYTE_PROGRAM_4BA_INSIZE 0x00 + +/* Sector Erase 0x21 (with 4-byte address), usually 4k size. + From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */ +#define JEDEC_SE_4BA 0x21 +#define JEDEC_SE_4BA_OUTSIZE 0x05 +#define JEDEC_SE_4BA_INSIZE 0x00 + +/* Block Erase 0x5C (with 4-byte address), usually 32k size. + From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */ +#define JEDEC_BE_5C_4BA 0x5C +#define JEDEC_BE_5C_4BA_OUTSIZE 0x05 +#define JEDEC_BE_5C_4BA_INSIZE 0x00 + +/* Block Erase 0xDC (with 4-byte address), usually 64k size. + From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */ +#define JEDEC_BE_DC_4BA 0xdc +#define JEDEC_BE_DC_4BA_OUTSIZE 0x05 +#define JEDEC_BE_DC_4BA_INSIZE 0x00 + /* enter 4-bytes addressing mode */ int spi_enter_4ba_b7(struct flashctx *flash); int spi_enter_4ba_b7_we(struct flashctx *flash); @@ -70,5 +100,15 @@ int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsig int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+/* read/write flash bytes with 4-bytes address from any mode (3-byte or 4-byte) */ +int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t databyte); +int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len); +int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len); + +/* erase flash bytes with 4-bytes address from any mode (3-byte or 4-byte) */ +int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +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); +
#endif /* __SPI_4BA_H__ */
From: Boris Baykov dev@borisbaykov.com
I've added progress visualization for read, erase and write operations. It's turned out that seeing progress is essential for reading and especially writing 32 MB of data via SPI. The operation can take more then 10 minutes on 15 MHz frequency of SPI. So, it's good to see its progress. I've added percents and slightly modified cli_output.c to send percents to screen only but not to logfile.
Patched files ------------- cli_output.c + print() patched to skip strings which are starting from '\b' to prevent writing progress percents to logfile
flash.h + added some definitions for progress visialization
flashrom.c + added progress visualization for erase/write (essensial for 32MB+ chips)
spi25.c + added progress visualization for read operation (essensial for 32MB+ chips)
Signed-off-by: Boris Baykov dev@borisbaykov.com, Russia, Jan 2014 [clg: ported from https://www.flashrom.org/pipermail/flashrom/2015-January/013203.html ] Signed-off-by: Cédric Le Goater clg@kaod.org --- cli_output.c | 3 ++- flash.h | 5 +++++ flashrom.c | 23 +++++++++++++++++++++++ spi25.c | 22 ++++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/cli_output.c b/cli_output.c index feafbd2017b1..cb82fa1a64f6 100644 --- a/cli_output.c +++ b/cli_output.c @@ -90,7 +90,8 @@ int print(enum msglevel level, const char *fmt, ...) fflush(output_type); } #ifndef STANDALONE - if ((level <= verbose_logfile) && logfile) { + /* skip of msgs starting from '\b' added to skip progress percents */ + if ((level <= verbose_logfile) && logfile && (!fmt || fmt[0] != '\b')) { va_start(ap, fmt); ret = vfprintf(logfile, fmt, ap); va_end(ap); diff --git a/flash.h b/flash.h index 7f79387a9251..0b72439f5ec9 100644 --- a/flash.h +++ b/flash.h @@ -360,6 +360,11 @@ __attribute__((format(printf, 2, 3))); #define msg_pspew(...) print(MSG_SPEW, __VA_ARGS__) /* programmer debug spew */ #define msg_cspew(...) print(MSG_SPEW, __VA_ARGS__) /* chip debug spew */
+/* Read progress will be shown for reads more than 256KB */ +#define MIN_LENGTH_TO_SHOW_READ_PROGRESS 256 * 1024 +/* Read progress will be shown for erases and writes more than 64KB */ +#define MIN_LENGTH_TO_SHOW_ERASE_AND_WRITE_PROGRESS 64 * 1024 + /* layout.c */ int register_include_arg(char *name); int process_include_args(void); diff --git a/flashrom.c b/flashrom.c index 6a6f5b6f6af8..28b177b0199b 100644 --- a/flashrom.c +++ b/flashrom.c @@ -1527,6 +1527,17 @@ static int walk_eraseregions(struct flashctx *flash, int erasefunction, unsigned int start = 0; unsigned int len; struct block_eraser eraser = flash->chip->block_erasers[erasefunction]; + int show_progress = 0; + unsigned int percent_last, percent_current; + unsigned long size = flash->chip->total_size * 1024; + + /* progress visualizaion init */ + if(size >= MIN_LENGTH_TO_SHOW_ERASE_AND_WRITE_PROGRESS) { + msg_cinfo(" "); /* only this space will go to logfile but all strings with \b wont. */ + msg_cinfo("\b 0%%"); + percent_last = percent_current = 0; + show_progress = 1; /* enable progress visualizaion */ + }
for (i = 0; i < NUM_ERASEREGIONS; i++) { /* count==0 for all automatically initialized array @@ -1544,8 +1555,20 @@ static int walk_eraseregions(struct flashctx *flash, int erasefunction, return 1; } start += len; + + if(show_progress) { + percent_current = (unsigned int) ((unsigned long long)start * 100 / size); + if(percent_current != percent_last) { + msg_cinfo("\b\b\b%2d%%", percent_current); + percent_last = percent_current; + } + } } } + + if(show_progress) + msg_cinfo("\b\b\b\b"); /* remove progress percents from the screen */ + msg_cdbg("\n"); return 0; } diff --git a/spi25.c b/spi25.c index b38c7444e873..93c4befacdc1 100644 --- a/spi25.c +++ b/spi25.c @@ -949,6 +949,16 @@ int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, int rc = 0; unsigned int i, j, starthere, lenhere, toread; unsigned int page_size = flash->chip->page_size; + int show_progress = 0; + unsigned int percent_last, percent_current; + + /* progress visualizaion init */ + if(len >= MIN_LENGTH_TO_SHOW_READ_PROGRESS) { + msg_cinfo(" "); /* only this space will go to logfile but all strings with \b wont. */ + msg_cinfo("\b 0%%"); + percent_last = percent_current = 0; + show_progress = 1; /* enable progress visualizaion */ + }
/* Warning: This loop has a very unusual condition and body. * The loop needs to go through each page with at least one affected @@ -976,8 +986,20 @@ int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, } if (rc) break; + + if(show_progress) { + percent_current = (unsigned int) ((unsigned long long)(starthere + + lenhere - start) * 100 / len); + if(percent_current != percent_last) { + msg_cinfo("\b\b\b%2d%%", percent_current); + percent_last = percent_current; + } + } }
+ if(show_progress && !rc) + msg_cinfo("\b\b\b\b"); /* remove progress percents from the screen */ + return rc; }
Hi Cédric, Thanks for the patches. Indeed, 4BA is a feature that we've been looking at for a while. I'll look forward to reviewing these patches in detail.
There are a few cases that need to be considered: - Chips which have a command to switch to 4BA mode - Chips which have a parallel instruction set for use with 4BA. - Chips that exclusively use 4BA commands.
I tried to cover the first two cases in my patch: https://chromium-review.googlesource.com/#/c/323359/ The third case was brought to our attention by Tim Chick: http://patchwork.coreboot.org/patch/4437/
Hopefully we can come up with a good solution for all cases.
On Sat, Jun 11, 2016 at 9:28 AM, Cédric Le Goater clg@kaod.org wrote:
Hi,
Here's an old patchset proposed a while ago by Boris Baykov to add 4byte support to flashrom. I have been using it to flash N25Q256 modules from a raspberry pi quite successfully. Could we restart a review cycle to see what needs fixing ?
A port of the complete patchset on 0.9.9 is available here :
https://github.com/legoater/flashrom
Thanks,
C.
Boris Baykov (6): 4BA: Basic support for 4-bytes addressing mode extensions 4BA: Flashrom integration for the 4-bytes addressing extensions 4BA: Winbond W25Q256.V chip (32MB) declaration, 4-bytes addr mode 4BA: Support for 4-bytes addressing via Extended Address Register 4BA: Support for new direct-4BA instructions + W25Q256.V update 4BA: Progress visualization for long read, writes and erases
Makefile | 2 +- chipdrivers.h | 22 ++ cli_output.c | 3 +- flash.h | 21 ++ flashchips.c | 48 +++ flashrom.c | 61 ++++ serprog.c | 5 +- spi.c | 5 +- spi25.c | 37 ++- spi4ba.c | 920 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ spi4ba.h | 114 ++++++++ 11 files changed, 1231 insertions(+), 7 deletions(-) create mode 100644 spi4ba.c create mode 100644 spi4ba.h
-- 2.1.4
flashrom mailing list flashrom@flashrom.org https://www.flashrom.org/mailman/listinfo/flashrom