Boris,
Thank you for your effort! This is indeed a very useful feature that flashrom has been lacking for too long.
It is very nice that you not only implemented support for the Winbond chip you had, but also took time to implement support for JESD216B specification and other variants of 4-byte addressing.
As you may know, I'm not a flashrom maintainer, but as we agreed I have reviewed your patch and tested it with Winbond W25Q256.V (the one I gave you) and also with MXIC MX25L25635F.
Here are some review comments:
- Equational macros (e.g. MIN_LENGTH_TO_SHOW_READ_PROGRESS) are defined without using parentheses around equations. That is risk-prone.
- In doit() the case when write is direct and the erasers are not is not handled, thus resulting in entering 4BA mode unnecessarily for direct writes.
- Conditions for use of direct/indirect functions are vague and error-prone. You could use feature bits inside some generic function to enter/exit 4BA mode as needed. The current approach of entering the 4BA mode just once in doit() and never exiting it looks risky to me. The same relates to 4BA/usual functions. IMO, there is no need for most of the *_4ba() functions as the only difference from usual functions is address size. Feature bits could be used again. Copy-pasting the code is a brute force approach and leads, in the long run, to nasty bugs when one fixes or enhances one function and forgets to update dozens of siblings/copycats. Similar code must be contained within as little entities as possible. The level of copy-pasteness of the original flashrom code is already excessive and increasing it further is not the best idea.
- Use of a fishing-world word 'BAIT' many times throughout the code. Probably, 'BYTE' was intended. This typo is quite alarming for me.
- In spi_chip_read() some comments are left irrelevant. The code after this patch no more checks if the chip fits between lowest valid and 0xFFFFFF (which is anyway not relevant for 4BA chips). The check is not performed at all for 4BA chips. ffs() never returns numbers higher than 32 as it takes a 32-bit integer as an argument, so checking if the return value is greater than 33 yields an always-false. ffsll() should probably be used instead (which would result in problems with compiling for Windows using mingw32).
- In walk_eraseregions() variables percent_last and percent_current are initialized conditionally, which makes some compilers (e.g., mingw32msvc-gcc v4.2.1) complain about possible use of those variables uninitialized.
- In doit() the comment in the last 'else' and the debug message are incorrect/irrelevant. In fact this code block is only executed for all-direct case, not for always-4ba case, which is handled separately in the first 'if'. As to the debug message, saying 'Using direct 4-byte addressing commands' would be more informative than 'No need to switch...'
- The change to serprog.c is hard to read. I would prefer a normal if/else instead of a ternary operator. That would be easier to read and understand. That's not the only place with ternary operator used with such long and complex arguments. This comment applies to all of those cases although if my earlier comment regarding copy-pasteness and superfluous _4ba functions is addressed, those monstrous ternary operators may just automagically go away.
- Happy New Year! I believe that the 'History of changes' in sfdp.c should contain dates from 2015, not from 2014. In other newly added places I guess it is also 2015, not 2014. ;)
- Looks like currently one can define a chip with size over 16MB but without FEATURE_4BA_SUPPORT and get strange results. Maybe it would be better to imply 4BA support for any chips over 16MB and get rid of FEATURE_4BA_SUPPORT bit?
- What is this 'eraser.type' field is for? I don't see any actual use for it. Looks like an index field to me if I understand JESD216B right. Wouldn't it be more correct to search for a proper eraser using block size rather than 'eraser type' ? Current implementation forces developers to think about those 'eraser types' each time they add a new 4BA chip. Am I overlooking anything here?
- I hate magic numbers. They are a source of hard-to-identify bugs. I know that it wasn't you who originally put it into sfdp.c/sfdp_fill_flash(), but since you added 16 * 4 to the existing 9 * 4, I would expect at least an added comment as to what those 4, 9 and 16 are. Better yet would be to replace them with meaningfully named macros.
- Things like this:
+ tmp32 = ((unsigned int)buf[(4 * 0) + 0]); + tmp32 |= ((unsigned int)buf[(4 * 0) + 1]) << 8; + tmp32 |= ((unsigned int)buf[(4 * 0) + 2]) << 16; + tmp32 |= ((unsigned int)buf[(4 * 0) + 3]) << 24;
make my eyes bleed. Why not use a loop or at least a macro?
Besides, this operation looks to me like a usual big-endian to host-endian conversion, or in other words a network byte order to host byte order conversion. If so, then it's definitely better, for the sake of portability, to use a standard ntohl() function here and not to reinvent a non-portable architecture-bound bicycle.
- Having boolean variables for each possible SFDP version (having in mind that more may come in future) is a bad idea, IMO. Keeping SFDP version in a variable outside the flashctx structure and passing it as an argument to functions along with the flashctx structure pointer is an even worse idea. SFDP version is apparently a property of the chip. Hence it should be contained within the chip information structure. However I would recommend to limit SFDP revision number awareness to the probe_spi_sfdp() function and convert the revision number therein to feature bits like FEATURE_SFDP_4BA_SUPPORT (for SFDP 1.6+), etc. That is, sfdp_fill_flash() shouldn't take an extra 'sfdp_rev_15' argument and instead should operate purely based on feature bits.
- Added parentheses in calls to msg_cspew around "(Type %d)" in sfdp.c someplaces broke the meaning of the phrases. Why did you do that for? It's about "Erase Sector Type", not about "Erase Sector". In most cases that was the only change. I would revoke those changes (except for adding a missing '+ 1' in one of the calls).
- Special handling for '\b' in msg_cinfo() looks kludgy to me. I would probably change print() to have an extra argument like 'console_only' and created an extra macro for calling print() with that argument being false. That macro I would use instead of msg_cinfo() to output progress. Or maybe it can be managed using verbosity alone. Anyway that would be less obscure for other developers during their future efforts than this special '\b' handling.
Also, I couldn't manage to make this patch work with MXIC MX25L25635F chip which supports all the 4BA modes (direct, EN4B, EAR). I tried various combinations, but it couldn't properly erase the chip anyway. Didn't work in SFDP mode as well (only SFDP 1.0 is supported by the chip). Hence, I couldn't check if it reads back properly what was written. I didn't do any debugging though to find out the reason.
The patch does indeed work for Winbond W25Q256.V.
Thanks again.
With best regards - Alexander Amelkin, T-Platforms [http://www.t-platforms.com]
P.S. Please note that the patch did not cleanly apply to r1868. I had to modify it.
-----Original Message----- From: flashrom [mailto:flashrom-bounces@flashrom.org] On Behalf Of Boris Baykov Sent: Sunday, January 11, 2015 2:24 AM To: flashrom@flashrom.org Subject: [flashrom] [PATCH/FULL/SVN] Support for 4-bytes addressing SPI flash chips 32MB+ (256Mb+)
This patch is for build 0.9.7-r1868 The patch is made by SVN diff. Also for review the patch is divided into 10 parts using diff(1).
Brief description ----------------- The patch adds ability to read, write and erase flash chips which require 4-bytes (32-bit) addressing to access to them. These are 32MB (256Mb) and larger new flash chips. Flashrom with this patch can support and program all (or most of all) of them. Besides of manually definable chips in flashchips.c this patch adds compliance to new JEDEC JESD216B standard which describes SFDP revision 1.6 procedure to read flash chip 4-bytes addressing details.
Patch structure --------------- To make code review easier I've splitted the patch in 10 incremental patches. These parts are made by standard Linux diff without using SVN. Also I've added 2 extra patches (4a & 5a) to show how to define chips using different types of 4-bytes addressing.
To apply the complete 4BA-patch you can directly apply SVN diff version of the patch or you should apply 10 patches ones by one in the following order: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.
Please note, that it's no need to apply patches 4a & 5a. Though you may apply patch 4a and the result code will work with no issues. But please don't try to use patch 5a because its code is not working with W25Q256*V chip, and this patch is here only to provide an example how to add chips with support of direct-4BA write and erase.
[PATCH 1/10] 4BA: Basic support for 4-bytes addressing mode extensions
[PATCH 2/10] 4BA: Flashrom integration for the 4-bytes addressing extensions
[PATCH 3/10] 4BA: Winbond W25Q256.V chip (32MB) declaration, 4-bytes addr mode
[PATCH 4/10] 4BA: Support for 4-bytes addressing via Extended Address Register
[PATCH 4a/10] 4BA: W25Q256.V declaration update to work using Ext.Addr.Reg The 4a patch is made to show how can you define chips with access via Extebded Address Register. This patch is fully working but not required.
[PATCH 5/10] 4BA: Support for new direct-4BA instructions + W25Q256.V update
[PATCH 5a/10] 4BA: W25Q256.V declaration update to work via direct funcs This patch doesn't work with this chip. It's this just to show how new direct-4ba functions can be used to define new chips.
[PATCH 6/10] 4BA: Progress visualization for long read, writes and erases
[PATCH 7/10] 4BA: SFDP 1.6 parser for compliance with JESD216B
[PATCH 8/10] 4BA: SFDP 1.6 simulation (for testing purposes)
[PATCH 9/10] 4BA: SFDP downgrade to 1.5 to support new Micron chips Explanation why 1.6 downgraded to 1.5 is below.
[PATCH 10/10] 4BA: Some small visual changes for 4-byte addresses
Full description ---------------- This patch includes code for three types of 4-byte addressing:
1. Flash chip can be switched to 4-bytes addressing mode and then all read/erase/program instructions will be switched from 3-bytes mode to 4-bytes mode. So, well known instructions like 03h (Read), 02h (Program) and 20h,52h,D8h (Erase) are becoming one byte longer and accept 4-bytes address instead of 3-bytes. 4-bytes addressing mode is entered by the instruction B7h with preceding Write Enable (WREN). This is default method.
2. Also Flash chip data with addresses more than 24-bit field can address may be accessed by use of Extended Address Register that has 1-byte size and stores high byte of 32-bit address. So, we read flash from old 3-bytes addressing mode and write high byte of address to special register. Then we have access to full memory of a chip. Some chips may support this method only.
3. Also some new commands avalable and implemented. Large size flash chips support special instructions to use 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 commands 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.
The patch adds code for all 3 types of 4-bytes address access and the exact method should be choosen by person who will manage to add new large chips to flashchips.c source file.
I've added one new flash chip: Winbond W25Q256FV (32MB) because this is the only chip I have now for testing.
Besides of manual including of new chips to Flashrom we may use SFDP procedure to discover parameters of the exact flash chip. More than a year ago JEDEC released new standard JESD216 (revision B) which describes in detail how to receive 4-bytes addressing settings from the chip. This patch provides new code to add compliance to JESD216B to Flashrom. This means that Flashrom now can automatically detect 4-bytes addressing features of large flash chips and use them to access data.
However not all things are so good. Unfortunately JESD216B had come to manufacturer's production lines just 2-3 months ago. So, we have many chips that have large size requires 32-bit addressing but their SFDP tables have revision lower than 1.6 (described in JESD216B standard). For example my W25Q256FV returns SFDP info with revision 1.0, first one :-) This situation leads us to requirement to try to work now without new SFDP info. And it's implemented in the patch too.
To test my parser code for SFDP revision 1.6 (from JESD216B) I've added some simulation code to sfdp.c. This code builds fake SFDP 1.6 tables in memory and passes them to the SFDP parser. Surely this simulation code is switched off by default and can be activated via hidden define on the top of sfdp.c
After I added SFDP 1.6 parser I found in Micron's datasheet for MT25Q 512Mb chip (MT25QL512AB/MT25QU512AB) that the chip returnes SFDP revision 1.5 and simultaniously provides the required information about addressing mode switch, however all information about direct-4BA functions isn't provided. Then I decided to parse new SFDP 4-bytes info from SFDP revision 1.5 besides of direct-4BA instructions. This assumption is made for better support of Micron flash chips. This should be checked and may be fixed later.
Also 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.
Testing results --------------- Both patches are tested with Winbond W25Q256FV flash chip using Olimex ARM-USB-OCD-H programmer.
Read, Erase and Program functions are fully tested for 4-bytes addressing type 1 (4-bytes mode), type 2 (extended addr. register) and partially tested for type 3 (new 4-bytes instructions with 4-bytes address). My W25Q256FV has support for 4BA_Read (13h) only, but doesn't support 4BA_Program (12h) and 4BA_Erase instructions. So, direct 4BA program and erase should be tested after.
Also I have to make an excuse about testing of SFDP rev 1.6 parser. I've tested it as good as I can using the simulation code I described above but I don't have ability to test it on real flash chips because I don't have a chip complied to JESD216B with SFDP 1.6 now. This should be tested later too.
Patched files ------------- chipdrivers.h + added functions declarations for spi4ba.c
cli_output.c + print() patched to skip strings which are starting from '\b' to prevent writing progress percents to logfile
flash.h + feature definitions added + added set of 4-bytes address functions to flashchip structure definition + added type field to block_eraser structure + added some definitions for progress visialization
flashchips.c + added definition for Winbond W25Q256BV/W25Q256FV chips
flashrom.c + added switch to 4-bytes addressing more for chips which support it + added progress visualization for erase/write (essensial for 32MB+ chips) + changed in address print %06x to %08x to support 32MB+ chips + added line to the flashrom's banner about this patch (temporary)
Makefile + added spi4ba.c
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 + added progress visualization for read operation (essensial for 32MB+ chips)
sfdp.c + added compliance to JEDEC JESD216B standard (SFDP revision 1.6) + added parse of SFDP 4-Bytes Addressing features (probe_spi_sfdp,sfdp_fill_flash) + added parse of SFDP 4-Bytes Addressing Instruction Table (sfdp_parse_4ba_table) + added choosing of block_erasers for 4-bytes addressing (sfdp_*_uniform_eraser*) + added exact flash page_size recognision from 11th dword in SFDP rev 1.6 + added fix for Micron chips to read 16th dword stating from SFDP revision 1.5 + added simulation of JESD216B for any flash chips (just for testing)
Added files ----------- spi4ba.h + definitions for 4-bytes addressing JEDEC commands + definitions for 16th dword bits of SFDP Basic Flash Param Table (JESD216B) + definitions for SFDP 4-Bytes Addressing Instruction Table (JESD216B) + functions declarations from spi4ba.c (same as in chipdrivers.h, just to see)
spi4ba.c + functions for enter 4-bytes addressing mode + functions for write Extended Address Register + functions for read/write/erase in 4-bytes addressing mode + functions for read/write/erase with Extended Address Register + functions for read/write/erase directly with 4-bytes address (from any mode) + functions to choose block_eraser by opcode for 4-bytes addressing for sfdp.c + functions to select proper block_eraser (ext.reg or 4-bytes addr) for sfdp.c
Signed-off-by: Boris Baykov dev@borisbaykov.com, Russia, Jan 2014 --- Index: Makefile =================================================================== --- Makefile (revision 1868) +++ Makefile (working copy) @@ -355,11 +355,11 @@ # Flash chip drivers and bus support infrastructure.
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.
LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o Index: chipdrivers.h =================================================================== --- chipdrivers.h (revision 1868) +++ chipdrivers.h (working copy) @@ -192,6 +192,33 @@
/* en29lv640b.c */ 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); +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); +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); +erasefunc_t *spi_get_erasefn_from_opcode_4ba(uint8_t opcode); +erasefunc_t *spi_get_erasefn_from_opcode_4ba_direct(uint8_t opcode); +int spi_block_erase_20_4ba_selector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_52_4ba_selector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_d8_4ba_selector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); + #endif /* !__CHIPDRIVERS_H__ */ Index: cli_output.c =================================================================== --- cli_output.c (revision 1868) +++ cli_output.c (working copy) @@ -88,11 +88,12 @@ * time-critical operations. Don't slow them down by flushing. */ if (level != MSG_SPEW) 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); if (level != MSG_SPEW) fflush(logfile); Index: flash.h =================================================================== --- flash.h (revision 1868) +++ flash.h (working copy) @@ -121,10 +121,18 @@ #define FEATURE_WRSR_EWSR (1 << 6) #define FEATURE_WRSR_WREN (1 << 7) #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) +#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, NT = 1, /* Not tested */ BAD, /* Known to not work */ @@ -165,10 +173,18 @@ unsigned int total_size; /* Chip page size in bytes */ 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; enum test_state read; enum test_state erase; @@ -188,10 +204,11 @@ * The first one that fits will be chosen. There is currently no way to * influence that behaviour. For testing just comment out the other * elements or set the function pointer to NULL. */ struct block_eraser { + int type; /* eraser type 1,2,3 or 4 */ struct eraseblock { unsigned int size; /* Eraseblock size in bytes */ unsigned int count; /* Number of contiguous blocks with that size */ } eraseblocks[NUM_ERASEREGIONS]; /* a block_erase function should try to erase one block of size @@ -339,10 +356,15 @@ #define msg_cdbg2(...) print(MSG_DEBUG2, __VA_ARGS__) /* chip debug2 */ #define msg_gspew(...) print(MSG_SPEW, __VA_ARGS__) /* general debug spew */ #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); int read_romlayout(const char *name); int normalize_romentries(const struct flashctx *flash); Index: flashchips.c =================================================================== --- flashchips.c (revision 1868) +++ flashchips.c (working copy) @@ -13320,10 +13320,58 @@ .voltage = {2700, 3600}, },
{ .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 | 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_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 */ + }, + .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, .model_id = WINBOND_NEX_W25Q20_W, .total_size = 256, Index: flashrom.c =================================================================== --- flashrom.c (revision 1868) +++ flashrom.c (working copy) @@ -1366,12 +1366,12 @@ "non-empty erase function. Not an error.\n"); if (!done) continue; if (done != chip->total_size * 1024) { msg_gerr("ERROR: Flash chip %s erase function %i " - "region walking resulted in 0x%06x bytes total," - " expected 0x%06x bytes. Please report a bug at" + "region walking resulted in 0x%08x bytes total," + " expected 0x%08x bytes. Please report a bug at" " flashrom@flashrom.org\n", chip->name, k, done, chip->total_size * 1024); ret = 1; } if (!eraser.block_erase) @@ -1460,29 +1460,52 @@ { int i, j; 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 * members so the loop below won't be executed for them. */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { /* Print this for every block except the first one. */ if (i || j) msg_cdbg(", "); - msg_cdbg("0x%06x-0x%06x", start, + msg_cdbg("0x%08x-0x%08x", start, start + len - 1); if (do_something(flash, start, len, param1, param2, eraser.block_erase)) { 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; }
static int check_block_eraser(const struct flashctx *flash, int k, int log) @@ -1934,10 +1957,48 @@ * erase and write. */ 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"); + } + /* 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 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"); + 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"); + } + /* 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) { return read_flash_to_file(flash, filename); }
oldcontents = malloc(size); Index: serprog.c =================================================================== --- serprog.c (revision 1868) +++ serprog.c (working copy) @@ -935,11 +935,14 @@ unsigned int i, cur_len; const unsigned int max_read = spi_master_serprog.max_data_read; 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; } return 0; } Index: sfdp.c =================================================================== --- sfdp.c (revision 1868) +++ sfdp.c (working copy) @@ -1,9 +1,10 @@ /* * This file is part of the flashrom project. * * Copyright (C) 2011-2012 Stefan Tauner + * 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. * @@ -15,17 +16,32 @@ * 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 */
+/* + * History of changes: + * 05/01/2014 Added compliance to JESD216B standard and SFDP revision 1.6 + * 07/01/2014 Modified to support SFDP revision 1.5 (for Micron flash chips) + */ + #include <stdint.h> #include <stdlib.h> #include <string.h> #include "flash.h" #include "spi.h" +#include "spi4ba.h" #include "chipdrivers.h"
+/* Default four bytes addressing behavior: + 1) 4-Bytes Addressing Mode (FBA_USE_EXT_ADDR_REG_BY_DEFAULT not defined) + 2) 3-bytes mode with Ext.Addr.Register (FBA_USE_EXT_ADDR_REG_BY_DEFAULT defined) */ +/* #define FBA_USE_EXT_ADDR_REG_BY_DEFAULT 1 */ + +/* For testing purposes only. Tests JESD216B SFDP compliance without proper flash chip */ +/* #define JESD216B_SIMULATION 1 */ + static int spi_sfdp_read_sfdp_chunk(struct flashctx *flash, uint32_t address, uint8_t *buf, int len) { int i, ret; uint8_t *newbuf; const unsigned char cmd[JEDEC_SFDP_OUTSIZE] = { @@ -72,22 +88,25 @@ } return ret; }
struct sfdp_tbl_hdr { - uint8_t id; + uint16_t id; uint8_t v_minor; uint8_t v_major; uint8_t len; uint32_t ptp; /* 24b pointer */ };
-static int sfdp_add_uniform_eraser(struct flashchip *chip, uint8_t opcode, uint32_t block_size) +static int sfdp_add_uniform_eraser(struct flashchip *chip, int eraser_type, uint8_t opcode, uint32_t block_size) { int i; uint32_t total_size = chip->total_size * 1024; - erasefunc_t *erasefn = spi_get_erasefn_from_opcode(opcode); + + /* choosing different eraser functions for 3-bytes and 4-bytes addressing */ + erasefunc_t *erasefn = (chip->feature_bits & FEATURE_4BA_SUPPORT) ? + spi_get_erasefn_from_opcode_4ba(opcode) : spi_get_erasefn_from_opcode(opcode);
if (erasefn == NULL || total_size == 0 || block_size == 0 || total_size % block_size != 0) { msg_cdbg("%s: invalid input, please report to " "flashrom@flashrom.org\n", __func__); @@ -109,10 +128,11 @@ msg_cspew(" Block Eraser %d is already occupied.\n", i); continue; }
+ eraser->type = eraser_type; eraser->block_erase = erasefn; eraser->eraseblocks[0].size = block_size; eraser->eraseblocks[0].count = total_size/block_size; msg_cdbg2(" Block eraser %d: %d x %d B with opcode " "0x%02x\n", i, total_size/block_size, block_size, @@ -123,44 +143,107 @@ " Please report this at flashrom@flashrom.org\n", __func__, i); return 1; }
-static int sfdp_fill_flash(struct flashchip *chip, uint8_t *buf, uint16_t len) +/* Try of replace exist erasers to new direct 4-bytes addressing erasers + which can be called from ANY addressing mode: 3-byte or 4-bytes. + These erasers opcodes defines in SFDP 4-byte address instruction table + from SFDP revision 1.6 that is defined by JESD216B standard. */ +static int sfdp_change_uniform_eraser_4ba_direct(struct flashchip *chip, int eraser_type, uint8_t opcode) +{ + int i; + erasefunc_t *erasefn = spi_get_erasefn_from_opcode_4ba_direct(opcode); + + if (erasefn == NULL) { + msg_cdbg("%s: invalid input, please report to " + "flashrom@flashrom.org\n", __func__); + return 1; + } + + for (i = 0; i < NUM_ERASEFUNCTIONS; i++) { + struct block_eraser *eraser = &chip->block_erasers[i]; + if (eraser->eraseblocks[0].size == 0) + break; + if (eraser->type != eraser_type) + continue; + + eraser->block_erase = erasefn; + msg_cdbg2(" Block eraser %d (type %d) changed to opcode " + "0x%02x\n", i, eraser_type, opcode); + return 0; + } + + msg_cspew("%s: Block Eraser type %d isn't found." + " Please report this at flashrom@flashrom.org\n", + __func__, eraser_type); + return 1; +} + +/* Parse of JEDEC SFDP Basic Flash Parameter Table */ +static int sfdp_fill_flash(struct flashchip *chip, uint8_t *buf, uint16_t len, int sfdp_rev_15) { uint8_t opcode_4k_erase = 0xFF; uint32_t tmp32; uint8_t tmp8; uint32_t total_size; /* in bytes */ uint32_t block_size; int j;
msg_cdbg("Parsing JEDEC flash parameter table... "); - if (len != 9 * 4 && len != 4 * 4) { + if (len != 16 * 4 && len != 9 * 4 && len != 4 * 4) { msg_cdbg("%s: len out of spec\n", __func__); return 1; } msg_cdbg2("\n"); - + /* 1. double word */ tmp32 = ((unsigned int)buf[(4 * 0) + 0]); tmp32 |= ((unsigned int)buf[(4 * 0) + 1]) << 8; tmp32 |= ((unsigned int)buf[(4 * 0) + 2]) << 16; tmp32 |= ((unsigned int)buf[(4 * 0) + 3]) << 24;
+ chip->feature_bits = 0; + tmp8 = (tmp32 >> 17) & 0x3; switch (tmp8) { case 0x0: msg_cdbg2(" 3-Byte only addressing.\n"); break; case 0x1: msg_cdbg2(" 3-Byte (and optionally 4-Byte) addressing.\n"); +#ifndef FBA_USE_EXT_ADDR_REG_BY_DEFAULT + /* assuming that 4-bytes addressing mode can be entered + by CMD B7h preceded with WREN and all read, write and + erase commands will be able to receive 4-bytes address */ + chip->feature_bits |= FEATURE_4BA_SUPPORT; + chip->four_bytes_addr_funcs.enter_4ba = spi_enter_4ba_b7_we; + chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba; + chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba; + chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba; +#else /* if FBA_USE_EXT_ADDR_REG_BY_DEFAULT defined */ + /* assuming that 4-bytes addressing is working using + extended address register which can be assigned + throught CMD C5h and then all commands will use + 3-bytes address as usual */ + chip->feature_bits |= ( FEATURE_4BA_SUPPORT | + FEATURE_4BA_EXTENDED_ADDR_REG ); + chip->four_bytes_addr_funcs.enter_4ba = NULL; + chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba_ereg; + chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba_ereg; + chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba_ereg; +#endif /* FBA_USE_EXT_ADDR_REG_BY_DEFAULT */ break; case 0x2: - msg_cdbg(" 4-Byte only addressing (not supported by " - "flashrom).\n"); - return 1; + msg_cdbg2(" 4-Byte only addressing.\n"); + chip->feature_bits |= ( FEATURE_4BA_SUPPORT | + FEATURE_4BA_ONLY ); + chip->four_bytes_addr_funcs.enter_4ba = NULL; + chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba; + chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba; + chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba; + break; default: msg_cdbg(" Required addressing mode (0x%x) not supported.\n", tmp8); return 1; } @@ -168,21 +251,21 @@ msg_cdbg2(" Status register is "); if (tmp32 & (1 << 3)) { msg_cdbg2("volatile and writes to the status register have to " "be enabled with "); if (tmp32 & (1 << 4)) { - chip->feature_bits = FEATURE_WRSR_WREN; + chip->feature_bits |= FEATURE_WRSR_WREN; msg_cdbg2("WREN (0x06).\n"); } else { - chip->feature_bits = FEATURE_WRSR_EWSR; + chip->feature_bits |= FEATURE_WRSR_EWSR; msg_cdbg2("EWSR (0x50).\n"); } } else { msg_cdbg2("non-volatile and the standard does not allow " "vendors to tell us whether EWSR/WREN is needed for " "status register writes - assuming EWSR.\n"); - chip->feature_bits = FEATURE_WRSR_EWSR; + chip->feature_bits |= FEATURE_WRSR_EWSR; }
msg_cdbg2(" Write chunk size is "); if (tmp32 & (1 << 2)) { msg_cdbg2("at least 64 B.\n"); @@ -212,56 +295,305 @@ return 1; } total_size = ((tmp32 & 0x7FFFFFFF) + 1) / 8; chip->total_size = total_size / 1024; msg_cdbg2(" Flash chip size is %d kB.\n", chip->total_size); + if (total_size > (1 << 24)) { - msg_cdbg("Flash chip size is bigger than what 3-Byte addressing " - "can access.\n"); - return 1; + if(!sfdp_rev_15) { + msg_cdbg("Flash chip size is bigger than what 3-Byte addressing " + "can access but chip's SFDP revision is lower than 1.6 " + "(1.5).\nConsequently 4-bytes addressing can NOT be " + "properly configured using current SFDP information.\n"); +#ifndef FBA_USE_EXT_ADDR_REG_BY_DEFAULT + msg_cdbg("Assuming that 4-bytes addressing mode can be " + "entered by CMD B7h with WREN.\n"); +#else + msg_cdbg("Assuming that 4-bytes addressing is working via " + "an Extended Address Register which can be written " + "by CMD C5h.\n"); +#endif + } }
- if (opcode_4k_erase != 0xFF) - sfdp_add_uniform_eraser(chip, opcode_4k_erase, 4 * 1024); - /* FIXME: double words 3-7 contain unused fast read information */
- if (len == 4 * 4) { + if (len < 9 * 4) { msg_cdbg(" It seems like this chip supports the preliminary " "Intel version of SFDP, skipping processing of double " "words 3-9.\n"); + + /* in the case if BFPT erasers array is not present + trying to add default 4k-eraser */ + if (opcode_4k_erase != 0xFF) + sfdp_add_uniform_eraser(chip, 0, opcode_4k_erase, 4 * 1024); + goto done; }
- /* 8. double word */ + /* 8. double word & 9. double word */ + /* for by block eraser types, from Type 1 to Type 4 */ for (j = 0; j < 4; j++) { /* 7 double words from the start + 2 bytes for every eraser */ tmp8 = buf[(4 * 7) + (j * 2)]; - msg_cspew(" Erase Sector Type %d Size: 0x%02x\n", j + 1, - tmp8); + msg_cspew(" Erase Sector (Type %d) Size: 0x%02x\n", j + 1, tmp8); if (tmp8 == 0) { - msg_cspew(" Erase Sector Type %d is unused.\n", j); + msg_cspew(" Erase Sector (Type %d) is unused.\n", j + 1); continue; } if (tmp8 >= 31) { - msg_cdbg2(" Block size of erase Sector Type %d (2^%d) " - "is too big for flashrom.\n", j, tmp8); + msg_cdbg2(" Block size of erase Sector (Type %d): 2^%d " + "is too big for flashrom.\n", j + 1, tmp8); continue; } block_size = 1 << (tmp8); /* block_size = 2 ^ field */
tmp8 = buf[(4 * 7) + (j * 2) + 1]; - msg_cspew(" Erase Sector Type %d Opcode: 0x%02x\n", j + 1, - tmp8); - sfdp_add_uniform_eraser(chip, tmp8, block_size); + msg_cspew(" Erase Sector (Type %d) Opcode: 0x%02x\n", j + 1, tmp8); + sfdp_add_uniform_eraser(chip, j + 1, tmp8, block_size); + } + + /* Trying to add the default 4k eraser after parsing erasers info. + In most cases this eraser has already been added before. */ + if (opcode_4k_erase != 0xFF) + sfdp_add_uniform_eraser(chip, 0, opcode_4k_erase, 4 * 1024); + + /* Trying to read the exact page size if it's available */ + if (len >= 11 * 4) { + /* 11. double word */ + tmp8 = buf[(4*10) + 0] >> 4; /* get upper nibble of LSB of 11th dword */ + chip->page_size = 1 << tmp8; /* page_size = 2 ^ N */ + msg_cdbg2(" Page size is %d B.\n", chip->page_size); + } + + /* If the chip doesn't support 4-bytes addressing mode we don't have + to read and analyze 16th DWORD of Basic Flash Parameter Table */ + if (!(chip->feature_bits & FEATURE_4BA_SUPPORT)) + goto done; + + /* In the case if the chip is working in 4-bytes addressing mode ONLY we + don't have to read and analyze 16th DWORD of Basic Flash Parameter Table + because we don't have to know how to switch to 4-bytes mode and back + when we are already in 4-bytes mode permanently. */ + if (chip->feature_bits & FEATURE_4BA_ONLY) + goto done; + + /* If the SFDP revision supported by the chip is lower that 1.6 (1.5) + we can not read and analyze 16th DWORD of Basic Flash Parameter Table. + Using defaults by FBA_USE_EXT_ADDR_REG_BY_DEFAULT define. */ + if(!sfdp_rev_15) + goto done; + + if (len < 16 * 4) { + msg_cdbg("%s: len of BFPT is out of spec\n", __func__); + msg_cerr("ERROR: Unable read 4-bytes addressing parameters.\n"); + return 1; + } + + /* 16. double word */ + tmp32 = ((unsigned int)buf[(4 * 15) + 0]); + tmp32 |= ((unsigned int)buf[(4 * 15) + 1]) << 8; + tmp32 |= ((unsigned int)buf[(4 * 15) + 2]) << 16; + tmp32 |= ((unsigned int)buf[(4 * 15) + 3]) << 24; + + /* Parsing 16th DWORD of Basic Flash Parameter Table according to JESD216B */ + + if(tmp32 & JEDEC_BFPT_DW16_ENTER_B7) { + msg_cdbg2(" Enter 4-bytes addressing mode by CMD B7h\n"); + chip->four_bytes_addr_funcs.enter_4ba = spi_enter_4ba_b7; + chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba; + chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba; + chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba; + /* if can go to 4BA-mode -> not need to use Ext.Addr.Reg */ + chip->feature_bits &= ~FEATURE_4BA_EXTENDED_ADDR_REG; + } + else if(tmp32 & JEDEC_BFPT_DW16_ENTER_B7_WE) { + msg_cdbg2(" Enter 4-bytes addressing mode by CMD B7h with WREN\n"); + chip->four_bytes_addr_funcs.enter_4ba = spi_enter_4ba_b7_we; + chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba; + chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba; + chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba; + /* if can go to 4BA-mode -> not need to use Ext.Addr.Reg */ + chip->feature_bits &= ~FEATURE_4BA_EXTENDED_ADDR_REG; + } + else if(tmp32 & JEDEC_BFPT_DW16_ENTER_EXTENDED_ADDR_REG) { + msg_cdbg2(" Extended Address Register used for 4-bytes addressing\n"); + chip->four_bytes_addr_funcs.enter_4ba = NULL; + chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba_ereg; + chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba_ereg; + chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba_ereg; + /* this flag signals to all '*_selector' functions + to use Ext.Addr.Reg while erase operations */ + chip->feature_bits |= FEATURE_4BA_EXTENDED_ADDR_REG; + } + else { + msg_cerr("ERROR: Unable to use 4-bytes addressing for this chip.\n" + " Please report this at flashrom@flashrom.org\n\n"); + return 1; }
done: msg_cdbg("done.\n"); return 0; }
+/* Parse of JEDEC SFDP 4-byte address instruction table. From SFDP revision 1.6 only. + This parsing shoukd be called after basic flash parameter table is parsed. */ +static int sfdp_parse_4ba_table(struct flashchip *chip, uint8_t *buf, uint16_t len) +{ + uint32_t tmp32; + uint8_t tmp8; + int j, direct_erasers; + int direct_count; + + msg_cdbg("Parsing JEDEC 4-byte address instuction table... "); + if (len != 2 * 4) { + msg_cdbg("%s: len out of spec\n", __func__); + return 1; + } + msg_cdbg2("\n"); + + /* 1. double word */ + tmp32 = ((unsigned int)buf[(4 * 0) + 0]); + tmp32 |= ((unsigned int)buf[(4 * 0) + 1]) << 8; + tmp32 |= ((unsigned int)buf[(4 * 0) + 2]) << 16; + tmp32 |= ((unsigned int)buf[(4 * 0) + 3]) << 24; + + direct_count = 0; + + if(tmp32 & JEDEC_4BAIT_READ_SUPPORT) { + msg_cdbg2(" Found Read CMD 13h with 4-bytes address\n"); + chip->four_bytes_addr_funcs.read_nbyte = spi_nbyte_read_4ba_direct; + /* read function has changed to direct 4-bytes function, + so entering 4-bytes mode isn't required for reading bytes */ + chip->feature_bits |= FEATURE_4BA_DIRECT_READ; + direct_count++; + } + + if(tmp32 & JEDEC_4BAIT_PROGRAM_SUPPORT) { + msg_cdbg2(" Found Write CMD 12h with 4-bytes address\n"); + chip->four_bytes_addr_funcs.program_byte = spi_byte_program_4ba_direct; + chip->four_bytes_addr_funcs.program_nbyte = spi_nbyte_program_4ba_direct; + /* write (program) functions have changed to direct 4-bytes functions, + so entering 4-bytes mode isn't required for writing bytes */ + chip->feature_bits |= FEATURE_4BA_DIRECT_WRITE; + direct_count++; + } + + direct_erasers = 0; + + /* 2. double word */ + for (j = 0; j < 4; j++) { + if(!(tmp32 & (JEDEC_4BAIT_ERASE_TYPE_1_SUPPORT << j))) + continue; + + tmp8 = buf[(4 * 1) + j]; + + msg_cdbg2(" Found Erase (type %d) CMD %02Xh with 4-bytes address\n", j + 1, tmp8); + + if(tmp8 == 0xFF) { + msg_cdbg("%s: Eraser (type %d) is supported, but opcode = 0xFF\n" + " Please report to flashrom@flashrom.org\n\n", __func__, j + 1); + continue; + } + + /* try of replacing the eraser with direct 4-bytes eraser */ + if(!sfdp_change_uniform_eraser_4ba_direct(chip, j + 1, tmp8)) + direct_erasers++; + } + + for (j = 0; j < NUM_ERASEFUNCTIONS; j++) { + if (chip->block_erasers[j].eraseblocks[0].size == 0) + break; + } + + if( j == direct_erasers ) { + /* if all erasers have been changed to direct 4-bytes ones, + then we don't have to enter 4-bytes mode for erase */ + chip->feature_bits |= FEATURE_4BA_ALL_ERASERS_DIRECT; + direct_count++; + msg_cspew("All erasers have changed to direct ones.\n"); + } + + if( direct_count == 3 ) { + /* if all read/write/erase functions are direct 4-bytes now, + then we don't have to use extended address register */ + chip->feature_bits &= ~FEATURE_4BA_EXTENDED_ADDR_REG; + msg_cspew("All read/write/erase functions have changed to direct ones.\n"); + } + + msg_cdbg("done.\n"); + return 0; +} + +#ifdef JESD216B_SIMULATION +/* This simulation increases size of Basic Flash Parameter Table + to have 16 dwords size and fills 16th dword with fake information + that is required to test JESD216B compliance. */ +int sfdp_jesd216b_simulation_dw16(uint8_t** ptbuf, uint16_t* plen) +{ + uint8_t* tbufsim; + uint16_t lensim = 16 * 4; + + tbufsim = malloc(lensim); + if (tbufsim == NULL) { + msg_gerr("Out of memory!\n"); + return 1; + } + + msg_cdbg("\n=== SIMULATION of JESD216B 16th Dword of Basic Flash Parameter Table\n"); + + memset(tbufsim, 0, 16 * 4); + memcpy(tbufsim, *ptbuf, min(*plen, 15 * 4)); + + tbufsim[(4*10) + 0] = 8 << 4; /* page size = 256 */ + + *((uint32_t*)&tbufsim[15 * 4]) = /*JEDEC_BFPT_DW16_ENTER_B7 | */ + JEDEC_BFPT_DW16_ENTER_B7_WE | + JEDEC_BFPT_DW16_ENTER_EXTENDED_ADDR_REG /* | + JEDEC_BFPT_DW16_ENTER_BANK_ADDR_REG_EN_BIT | + JEDEC_BFPT_DW16_ENTER_NV_CONFIG_REG | + JEDEC_BFPT_DW16_VENDOR_SET | + JEDEC_BFPT_DW16_4_BYTES_ADDRESS_ONLY */ ; + + free(*ptbuf); + *ptbuf = tbufsim; + *plen = lensim; + return 0; +} + +/* This simulation created fake 4-bytes Address Instruction Table + with features information to test JESD216B compliance. */ +int sfdp_jesd216b_simulation_4bait(uint8_t** ptbuf, uint16_t* plen) +{ + uint8_t* tbufsim; + uint16_t lensim = 2 * 4; + + tbufsim = malloc(lensim); + if (tbufsim == NULL) { + msg_gerr("Out of memory!\n"); + return 1; + } + + msg_cdbg("\n=== SIMULATION of JESD216B 4-bytes Address Instruction Table\n"); + + *((uint32_t*)&tbufsim[0]) = JEDEC_4BAIT_READ_SUPPORT /*| + JEDEC_4BAIT_PROGRAM_SUPPORT | + JEDEC_4BAIT_ERASE_TYPE_1_SUPPORT | + JEDEC_4BAIT_ERASE_TYPE_2_SUPPORT | + JEDEC_4BAIT_ERASE_TYPE_3_SUPPORT | + JEDEC_4BAIT_ERASE_TYPE_4_SUPPORT */; + *((uint32_t*)&tbufsim[4]) = 0xFFFFFFFF; + /* *((uint32_t*)&tbufsim[4]) = 0xFFDC5C21; */ + + free(*ptbuf); + *ptbuf = tbufsim; + *plen = lensim; + return 0; +} +#endif + int probe_spi_sfdp(struct flashctx *flash) { int ret = 0; uint8_t buf[8]; uint32_t tmp32; @@ -269,10 +601,11 @@ /* need to limit the table loop by comparing i to uint8_t nph hence: */ uint16_t i; struct sfdp_tbl_hdr *hdrs; uint8_t *hbuf; uint8_t *tbuf; + int sfdp_rev_16 = 0, sfdp_rev_15 = 0;
if (spi_sfdp_read_sfdp(flash, 0x00, buf, 4)) { msg_cdbg("Receiving SFDP signature failed.\n"); return 0; } @@ -296,10 +629,36 @@ if (buf[1] != 0x01) { msg_cdbg("The chip supports an unknown version of SFDP. " "Aborting SFDP probe!\n"); return 0; } + + /* JEDEC JESD216B defines SFDP revision 1.6 and includes: + 1) 16 dwords in Basic Flash Parameter Table + 2) 16th dword has information how to enter + and exit 4-bytes addressing mode + 3) 4-Bytes Address Instruction Table with ID 0xFF84 + + However we can see in the datasheet for Micron's + MT25Q 512Mb chip (MT25QL512AB/MT25QU512AB) that the + chip returnes SFDP revision 1.5 and has 16 dwords + in its Basic Flash Paramater Table. Also the information + about addressing mode switch is exist in the 16th dword. + But 4-Bytes Address Instruction Table is absent. + + So we will use 16th dword from SFDP revision 1.5 + but 4-Bytes Address Instruction Table from SFDP 1.6 only. + This assumption is made for better support of Micron + flash chips. + + FIXME: SFDP revisions compliance should be checked + more carefully after more information about JESD216B + SFDP tables will be known from real flash chips. + */ + sfdp_rev_16 = (buf[1] == 1 && buf[0] >= 6) || buf[1] > 1; + sfdp_rev_15 = (buf[1] == 1 && buf[0] >= 5) || buf[1] > 1; + nph = buf[2]; msg_cdbg2("SFDP number of parameter headers is %d (NPH = %d).\n", nph + 1, nph);
/* Fetch all parameter headers, even if we don't use them all (yet). */ @@ -314,17 +673,18 @@ goto cleanup_hdrs; }
for (i = 0; i <= nph; i++) { uint16_t len; - hdrs[i].id = hbuf[(8 * i) + 0]; + hdrs[i].id = hbuf[(8 * i) + 0]; /* ID LSB read */ hdrs[i].v_minor = hbuf[(8 * i) + 1]; hdrs[i].v_major = hbuf[(8 * i) + 2]; hdrs[i].len = hbuf[(8 * i) + 3]; hdrs[i].ptp = hbuf[(8 * i) + 4]; hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 5]) << 8; hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 6]) << 16; + hdrs[i].id |= ((uint16_t)hbuf[(8 * i) + 7]) << 8; /* ID MSB read */ msg_cdbg2("\nSFDP parameter table header %d/%d:\n", i, nph); msg_cdbg2(" ID 0x%02x, version %d.%d\n", hdrs[i].id, hdrs[i].v_major, hdrs[i].v_minor); len = hdrs[i].len * 4; tmp32 = hdrs[i].ptp; @@ -366,29 +726,51 @@ } } msg_cspew("\n");
if (i == 0) { /* Mandatory JEDEC SFDP parameter table */ - if (hdrs[i].id != 0) + if (hdrs[i].id != JEDEC_BFPT_ID) msg_cdbg("ID of the mandatory JEDEC SFDP " - "parameter table is not 0 as demanded " - "by JESD216 (warning only).\n"); - + "parameter table is not 0xFF00 as" + "demanded by JESD216 (warning only)." + "\n"); +#ifdef JESD216B_SIMULATION + if(!sfdp_jesd216b_simulation_dw16(&tbuf, &len)) + sfdp_rev_16 = sfdp_rev_15 = 1; /* pretend as SFDP rev 1.6 */ +#endif if (hdrs[i].v_major != 0x01) { msg_cdbg("The chip contains an unknown " "version of the JEDEC flash " "parameters table, skipping it.\n"); - } else if (len != 9 * 4 && len != 4 * 4) { + } else if (len != 16 * 4 && len != 9 * 4 && len != 4 * 4) { msg_cdbg("Length of the mandatory JEDEC SFDP " "parameter table is wrong (%d B), " "skipping it.\n", len); - } else if (sfdp_fill_flash(flash->chip, tbuf, len) == 0) + } else if (sfdp_fill_flash(flash->chip, tbuf, len, sfdp_rev_15) == 0) ret = 1; +#ifdef JESD216B_SIMULATION + if(ret == 1 && !sfdp_jesd216b_simulation_4bait(&tbuf, &len)) + sfdp_parse_4ba_table(flash->chip, tbuf, len); +#endif + } + /* JEDEC SFDP 4-byte address instruction table. From SFDP revision 1.6 only. + This parsing shoukd be called after basic flash parameter table is parsed. */ + else if(sfdp_rev_16 && hdrs[i].id == JEDEC_4BAIT_ID && ret == 1) { + if (hdrs[i].v_major != 0x01) { + msg_cdbg("The chip contains an unknown " + "version of the JEDEC 4-bytes " + "address instruction table, " + "skipping it.\n"); + } + else { /* no result check because this table is optional */ + sfdp_parse_4ba_table(flash->chip, tbuf, len); + } } free(tbuf); }
cleanup_hdrs: free(hdrs); free(hbuf); return ret; } + Index: spi.c =================================================================== --- spi.c (revision 1868) +++ spi.c (working copy) @@ -108,11 +108,14 @@ /* Check if the chip fits between lowest valid and highest possible * address. Highest possible address with the current SPI implementation * 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. */ addrbase = (1 << 24) - flash->chip->total_size * 1024; } Index: spi25.c =================================================================== --- spi25.c (revision 1868) +++ spi25.c (working copy) @@ -26,10 +26,11 @@ #include "flash.h" #include "flashchips.h" #include "chipdrivers.h" #include "programmer.h" #include "spi.h" +#include "spi4ba.h"
static int spi_rdid(struct flashctx *flash, unsigned char *readarr, int bytes) { static const unsigned char cmd[JEDEC_RDID_OUTSIZE] = { JEDEC_RDID }; int ret; @@ -946,10 +947,20 @@ unsigned int len, unsigned int chunksize) { 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 * byte. The lowest page number is (start / page_size) since that * division rounds down. The highest page number we want is the page @@ -964,18 +975,33 @@ starthere = max(start, i * page_size); /* Length of bytes in the range in this page. */ 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; } 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; }
/* * Write a part of the flash chip. @@ -1009,11 +1035,14 @@ starthere = max(start, i * page_size); /* Length of bytes in the range in this page. */ 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) programmer_delay(10); } @@ -1035,11 +1064,13 @@ { unsigned int i; 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) programmer_delay(10); } Index: spi4ba.c =================================================================== --- spi4ba.c (revision 0) +++ spi4ba.c (working copy) @@ -0,0 +1,1020 @@ +/* + * 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; +} + +/* 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; +} + +/* 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; +} + +/* Selector for 4k eraser that chooses between 4-bytes addressing mode + and use of Extended Address Register from 3-bytes addressing mode */ +int spi_block_erase_20_4ba_selector(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + msg_trace("-> %s\n", __func__); + + return (flash->chip->feature_bits & FEATURE_4BA_EXTENDED_ADDR_REG) ? + spi_block_erase_20_4ba_ereg(flash, addr, blocklen) : + spi_block_erase_20_4ba(flash, addr, blocklen); +} + +/* Selector for 32k eraser that chooses between 4-bytes addressing mode + and use of Extended Address Register from 3-bytes addressing mode */ +int spi_block_erase_52_4ba_selector(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + msg_trace("-> %s\n", __func__); + + return (flash->chip->feature_bits & FEATURE_4BA_EXTENDED_ADDR_REG) ? + spi_block_erase_52_4ba_ereg(flash, addr, blocklen) : + spi_block_erase_52_4ba(flash, addr, blocklen); +} + +/* Selector for 64k eraser that chooses between 4-bytes addressing mode + and use of Extended Address Register from 3-bytes addressing mode */ +int spi_block_erase_d8_4ba_selector(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + msg_trace("-> %s\n", __func__); + + return (flash->chip->feature_bits & FEATURE_4BA_EXTENDED_ADDR_REG) ? + spi_block_erase_d8_4ba_ereg(flash, addr, blocklen) : + spi_block_erase_d8_4ba(flash, addr, blocklen); +} + +/* Chooser for erase function by instruction opcode for block eraser instructions + to work with 4-bytes addressing flash chips. This chooser is called from sfdp.c + during parse of 8th & 9th dwords of SFDP Basic Flash Parameter Table */ +erasefunc_t *spi_get_erasefn_from_opcode_4ba(uint8_t opcode) +{ + msg_trace("-> %s\n", __func__); + + switch(opcode){ + case 0xff: + case 0x00: + /* Not specified, assuming "not supported". */ + return NULL; + case 0x20: + return &spi_block_erase_20_4ba_selector; /* selector */ + case 0x52: + return &spi_block_erase_52_4ba_selector; /* selector */ + case 0x60: + return &spi_block_erase_60; + case 0x62: + return &spi_block_erase_62; + case 0xc7: + return &spi_block_erase_c7; + case 0xd8: + return &spi_block_erase_d8_4ba_selector; /* selector */ + case 0x50: + case 0x81: + case 0xc4: + case 0xd7: + case 0xdb: + msg_cinfo("%s: erase opcode (0x%02x) doesn't have its 4-bytes addressing version." + " Please report this at flashrom@flashrom.org\n", __func__, opcode); + return NULL; + default: + msg_cinfo("%s: unknown erase opcode (0x%02x). Please report " + "this at flashrom@flashrom.org\n", __func__, opcode); + return NULL; + } +} + +/* Chooser for erase function by instruction opcode for block eraser instructions + which can be used from ANY mode (3-bytes or 4-bytes). This chooser is called + from sfdp.c during parse of SFDP 4-Bytes Address Instruction Table (JESD216B) */ +erasefunc_t *spi_get_erasefn_from_opcode_4ba_direct(uint8_t opcode) +{ + msg_trace("-> %s\n", __func__); + + switch(opcode){ + case 0xff: + case 0x00: + /* Not specified, assuming "not supported". */ + return NULL; + case 0x21: + return &spi_block_erase_21_4ba_direct; /* direct */ + case 0x5C: + return &spi_block_erase_5c_4ba_direct; /* direct */ + case 0xdc: + return &spi_block_erase_dc_4ba_direct; /* direct */ + default: + msg_cinfo("%s: unknown erase opcode (0x%02x). Please report " + "this at flashrom@flashrom.org\n", __func__, opcode); + return NULL; + } +}
Property changes on: spi4ba.c ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: spi4ba.h =================================================================== --- spi4ba.h (revision 0) +++ spi4ba.h (working copy) @@ -0,0 +1,157 @@ +/* + * 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 + +/* 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 + +/* 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 + +/* JEDEC Basic Flash Parameters Table definition */ +#define JEDEC_BFPT_ID 0xFF00 + +/* JEDEC Basic Flash Parameters Table 16th dword according to JESD216B */ +/* 16th dword of BFPT, bits 24:31 (Enter 4-Byte Addressing) */ +#define JEDEC_BFPT_DW16_ENTER_B7 (1UL << 24) +#define JEDEC_BFPT_DW16_ENTER_B7_WE (1UL << 25) +#define JEDEC_BFPT_DW16_ENTER_EXTENDED_ADDR_REG (1UL << 26) +#define JEDEC_BFPT_DW16_ENTER_BANK_ADDR_REG_EN_BIT (1UL << 27) +#define JEDEC_BFPT_DW16_ENTER_NV_CONFIG_REG (1UL << 28) +#define JEDEC_BFPT_DW16_VENDOR_SET (1UL << 29) +#define JEDEC_BFPT_DW16_4_BYTES_ADDRESS_ONLY (1UL << 30) +#define JEDEC_BFPT_DW16_ENTER_RESERVED (1UL << 31) +/* 16th dword of BFPT, bits 14:23 (Exit 4-Byte Addressing) */ +#define JEDEC_BFPT_DW16_EXIT_E9 (1UL << 14) +#define JEDEC_BFPT_DW16_EXIT_E9_WE (1UL << 15) +#define JEDEC_BFPT_DW16_EXIT_EXTENDED_ADDR_REG (1UL << 16) +#define JEDEC_BFPT_DW16_EXIT_BANK_ADDR_REG_EN_BIT (1UL << 17) +#define JEDEC_BFPT_DW16_EXIT_NV_CONFIG_REG (1UL << 18) +#define JEDEC_BFPT_DW16_EXIT_HARD_RESET (1UL << 19) +#define JEDEC_BFPT_DW16_EXIT_SOFT_RESET (1UL << 20) +#define JEDEC_BFPT_DW16_EXIT_POWER_CYCLE (1UL << 21) +#define JEDEC_BFPT_DW16_EXIT_RESERVED_1 (1UL << 22) +#define JEDEC_BFPT_DW16_EXIT_RESERVED_2 (1UL << 23) + +/* JEDEC 4-Bytes Addressing Table 1st dword according to JESD216B */ +#define JEDEC_4BAIT_ID 0xFF84 +#define JEDEC_4BAIT_READ_SUPPORT (1UL << 0) +#define JEDEC_4BAIT_PROGRAM_SUPPORT (1UL << 6) +#define JEDEC_4BAIT_ERASE_TYPE_1_SUPPORT (1UL << 9) +#define JEDEC_4BAIT_ERASE_TYPE_2_SUPPORT (1UL << 10) +#define JEDEC_4BAIT_ERASE_TYPE_3_SUPPORT (1UL << 11) +#define JEDEC_4BAIT_ERASE_TYPE_4_SUPPORT (1UL << 12) + +/* 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); + +/* 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); + +/* 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); + +/* erase functions choosers for 4-bytes addressing mode */ +erasefunc_t *spi_get_erasefn_from_opcode_4ba(uint8_t opcode); +erasefunc_t *spi_get_erasefn_from_opcode_4ba_direct(uint8_t opcode); + +/* selection of erase function between 4-bytes addressing mode and use of extended address register */ +int spi_block_erase_20_4ba_selector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_52_4ba_selector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_d8_4ba_selector(struct flashctx *flash, unsigned int addr, unsigned int blocklen); + + +#endif /* __SPI_4BA_H__ */
Property changes on: spi4ba.h ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property