Am 31.01.2012 06:59 schrieb Stefan Tauner: > Similar to modules using the opaque programmer framework (e.g. ICH Hardware > Sequencing) this uses a template struct flashchip element in flashchips.c with > a special probe function that fills the obtained values into that struct. > > This allows yet unknown SPI chips to be supported (read, erase, write) almost > as if it was already added to flashchips.c. > > Documentation used: > http://www.jedec.org/standards-documents/docs/jesd216 (2011-04) > W25Q32BV data sheet Revision F (2011-04-01) > EN25QH16 data sheet Revision F (2011-06-01) > > todo: > - handle programmers which have a problem with the dummy bytes needed
AMD SB[678]x0 SPI has a way to specify sending one dummy byte between write and read, IIRC it is called DropOneClkOnRead or somthing like that. Quite a few other SPI masters have the one-dummy-byte functionality as well. This needs to be implemented in a generic way (I have a totally bitrotted patch for it), but it should not hold back this patch. > - move sfdp_add_uniform_eraser to spi25.c for further use like > spi_get_erasefn_from_opcode? > - is setting the generic SPI unlock method safe? > - what page_size should be set? > > Tested-by: David Hendricks <dhend...@google.com> > on W25Q64CV > > Signed-off-by: Stefan Tauner <stefan.tau...@student.tuwien.ac.at> > --- > Makefile | 2 +- > chipdrivers.h | 4 + > flash.h | 2 + > flashchips.c | 23 ++++ > flashchips.h | 1 + > flashrom.c | 28 +++++- > sfdp.c | 355 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > spi.h | 5 + > spi25.c | 31 +++++ > 9 files changed, 449 insertions(+), 2 deletions(-) > create mode 100644 sfdp.c > > diff --git a/Makefile b/Makefile > index 83aa038..b890d8a 100644 > --- a/Makefile > +++ b/Makefile > @@ -252,7 +252,7 @@ endif > CHIP_OBJS = jedec.o stm50flw0x0x.o w39.o w29ee011.o \ > sst28sf040.o m29f400bt.o 82802ab.o pm49fl00x.o \ > sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o sharplhf00l04.o \ > - a25.o at25.o opaque.o > + a25.o at25.o opaque.o sfdp.o > > LIB_OBJS = layout.o > > diff --git a/chipdrivers.h b/chipdrivers.h > index a1d0cd9..bd81098 100644 > --- a/chipdrivers.h > +++ b/chipdrivers.h > @@ -41,6 +41,7 @@ int spi_block_erase_d7(struct flashctx *flash, unsigned int > addr, unsigned int b > int spi_block_erase_d8(struct flashctx *flash, unsigned int addr, unsigned > int blocklen); > int spi_block_erase_60(struct flashctx *flash, unsigned int addr, unsigned > int blocklen); > int spi_block_erase_c7(struct flashctx *flash, unsigned int addr, unsigned > int blocklen); > +erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode); > int spi_chip_write_1(struct flashctx *flash, uint8_t *buf, unsigned int > start, unsigned int len); > int spi_chip_write_256(struct flashctx *flash, uint8_t *buf, unsigned int > start, unsigned int len); > int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, > int unsigned len); > @@ -58,6 +59,9 @@ int spi_read_chunked(struct flashctx *flash, uint8_t *buf, > unsigned int start, u > int spi_write_chunked(struct flashctx *flash, uint8_t *buf, unsigned int > start, unsigned int len, unsigned int chunksize); > int spi_aai_write(struct flashctx *flash, uint8_t *buf, unsigned int start, > unsigned int len); > > +/* sfdp.c */ > +int probe_spi_sfdp(struct flashctx *flash); > + > /* opaque.c */ > int probe_opaque(struct flashctx *flash); > int read_opaque(struct flashctx *flash, uint8_t *buf, unsigned int start, > unsigned int len); > diff --git a/flash.h b/flash.h > index e51b6d4..6bcae71 100644 > --- a/flash.h > +++ b/flash.h > @@ -174,6 +174,8 @@ struct flashctx { > struct registered_programmer *pgm; > }; > > +typedef int (erasefunc_t)(struct flashctx *flash, unsigned int addr, > unsigned int blocklen); I believe that typedef to be pretty unreadable, but I see that avoiding the typedef would probably be even worse. > + > #define TEST_UNTESTED 0 > > #define TEST_OK_PROBE (1 << 0) > diff --git a/flashchips.c b/flashchips.c > index 0c8257b..b22e709 100644 > --- a/flashchips.c > +++ b/flashchips.c > @@ -8872,6 +8872,29 @@ const struct flashchip flashchips[] = { > .read = read_memmapped, > .voltage = {3000, 3600}, /* Also has 12V fast program */ > }, > + > + { > + .vendor = "Unknown", > + .name = "SFDP device", > + .bustype = BUS_SPI, > + .manufacture_id = GENERIC_MANUF_ID, > + .model_id = SFDP_DEVICE_ID, > + /* We present our own "report this" text hence we do not > + * want the default "This flash part has status UNTESTED..." > + * text to be printed. */ > + .tested = TEST_OK_PREW, > + .probe = probe_spi_sfdp, > + .unlock = spi_disable_blockprotect, /* is this safe? */ Should be safe AFAICS, but that's not a hard statement, it's a gut feeling. > + .read = spi_chip_read, > + .page_size = 256, /* ? */ Argh, page_size comes to bite us again. Did I already send my "kill most uses of page_size" patch? > + /* FIXME: some vendor extensions define this */ > + .voltage = {}, > + /* Everything below will be set by the probing function. */ > + .write = NULL, > + .total_size = 0, > + .feature_bits = 0, > + .block_erasers = {}, > + }, > > { > .vendor = "Unknown", > diff --git a/flashchips.h b/flashchips.h > index 03efb86..1f2a8ca 100644 > --- a/flashchips.h > +++ b/flashchips.h > @@ -36,6 +36,7 @@ > > #define GENERIC_MANUF_ID 0xffff /* Check if there is a vendor ID */ > #define GENERIC_DEVICE_ID 0xffff /* Only match the vendor ID */ > +#define SFDP_DEVICE_ID 0xfffe Side note: Should we move PROGMANUF_ID and its companion from the bottom of the file to this location to have generic match IDs in one place? > > #define ALLIANCE_ID 0x52 /* Alliance Semiconductor */ > #define ALLIANCE_AS29F002B 0x34 > diff --git a/flashrom.c b/flashrom.c > index ee68344..84fb3fc 100644 > --- a/flashrom.c > +++ b/flashrom.c > @@ -986,7 +986,33 @@ int probe_flash(struct registered_programmer *pgm, int > startchip, > * probe_flash() is the first one and thus no chip has been > * found before. The comment above is not really valid anymore. Can you include the following snippet in your patch? @@ -980,11 +980,10 @@ /* If this is the first chip found, accept it. * If this is not the first chip found, accept it only if it is - * a non-generic match. - * We could either make chipcount global or provide it as - * parameter, or we assume that startchip==0 means this call to - * probe_flash() is the first one and thus no chip has been - * found before. + * a non-generic match. SFDP and CFI are generic matches. + * startchip==0 means this call to probe_flash() is the first + * one for this programmer interface and thus no other chip has + * been found on this interface. */ if (startchip == 0 || fill_flash->model_id != GENERIC_DEVICE_ID) break; > */ > - if (startchip == 0 || fill_flash->model_id != GENERIC_DEVICE_ID) > + if (startchip == 0 && fill_flash->model_id == SFDP_DEVICE_ID) { > + msg_cinfo("===\n" > + "SFDP has autodetected a flash chip which is " > + "not natively supported by flashrom yet.\n"); > + if (count_usable_erasers(fill_flash) == 0) > + msg_cinfo("The standard operations read and " > + "verify should work, but to support " > + "erase, write and all other " > + "possible features"); > + else > + msg_cinfo("All standard operations (read, " > + "verify, erase and write) should " > + "work, but to support all possible " > + "features"); > + > + msg_cinfo(" we need to add them manually.\nYou " > + "can help us by mailing us the output of " > + "the following command to flashrom@flashrom." > + "org: \n'flashrom -VV [plus the " > + "-p/--programmer parameter (if needed)]" > + "'\nThanks for your help!\n" > + "===\n"); > + } > + > + if (startchip == 0 || > + ((fill_flash->model_id != GENERIC_DEVICE_ID) && > + (fill_flash->model_id != SFDP_DEVICE_ID))) > break; > > notfound: > diff --git a/sfdp.c b/sfdp.c > new file mode 100644 > index 0000000..7c00ff5 > --- /dev/null > +++ b/sfdp.c > @@ -0,0 +1,355 @@ > +/* > + * This file is part of the flashrom project. > + * > + * Copyright (C) 2011-2012 Stefan Tauner > + * > + * 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 > + */ > + > +#include <stdint.h> > +#include <stdlib.h> > +#include "flash.h" > +#include "spi.h" > +#include "chipdrivers.h" > + > +static int spi_sfdp_wrapper(struct flashctx *flash, uint32_t address, > uint8_t *buf, int len) > +{ > + int i, ret; > + const unsigned char cmd[JEDEC_SFDP_OUTSIZE] = { > + JEDEC_SFDP, > + (address >> 16) & 0xff, > + (address >> 8) & 0xff, > + (address >> 0) & 0xff, > + 0 > + }; > + msg_cspew("spi_sfdp_wrapper: addr=0x%x, len=%d, data:\n", address, len); > + ret = spi_send_command(flash, sizeof(cmd), len, cmd, buf); > + for (i = 0; i < len; i++) > + msg_cspew(" 0x%02x", buf[i]); > + msg_cspew("\n"); > + return ret; > +} > + > +/* FIXME: eventually something similar like this but more generic should be > + * available to split up spi commands. use that then instead */ > +static int spi_sfdp(struct flashctx *flash, uint32_t address, uint8_t *buf, > int len) > +{ > + /* FIXME: this is wrong. */ > + int maxstep = 8; Yes, maxstep should be a programmer-specific setting. However, with our current infrastructure there is no way to fix this easily. > + int ret = 0; > + while (len > 0) { > + int step = min(len, maxstep); > + ret = spi_sfdp_wrapper(flash, address, buf, step); > + if (ret) Actually, there is probably a way to determine optimal size for those SFDP requests: Check if ret indicates a "command too long" error and reduce maxstep by one in that case, then retry. Such code is not exactly pretty and I'm not sure if all SPI masters have such a differentiated error code reporting. > + return ret; > + address += step; > + buf += step; > + len -= step; > + } > + return ret; > +} > + > +struct sfdp_tbl_hdr { > + uint8_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 flashctx *f, uint8_t opcode, > uint32_t bsize) struct flashctx *flash Just "f" is too short, and if you try to search for it, you'll get thousands of matches you don't want. bsize or blocksize? I prefer the latter for consistency and readability reasons. > +{ > + uint32_t total_size = f->total_size * 1024; > + int i; > + erasefunc_t *erasefn = spi_get_erasefn_from_opcode(opcode); > + if (erasefn == NULL) > + return 1; > + for (i = 0; i < NUM_ERASEFUNCTIONS; i++) { > + struct block_eraser *eraser = &f->block_erasers[i]; > + if (eraser->eraseblocks[0].size != 0 || !eraser->block_erase) > + continue; > + eraser->block_erase = erasefn; > + eraser->eraseblocks[0].size = bsize; > + eraser->eraseblocks[0].count = total_size/bsize; > + msg_cdbg2(" Block eraser %d: %d x %d B with opcode " > + "0x%02x\n", i, total_size/bsize, bsize, > + opcode); > + return 0; > + } > + msg_cinfo("%s: Not enough space to store another eraser (i=%d)." msg_cerr? > + " Please report this at flashrom@flashrom.org\n", > + __func__, i); > + return 1; > +} > + Rest of review follows later. > +static int sfdp_fill_flash(struct flashctx *f, uint8_t *buf, uint16_t len) > +{ > + uint32_t tmp32; > + uint8_t tmp8; > + uint32_t total_size; /* in bytes */ > + uint32_t bsize; > + uint8_t opcode_4k = 0xFF; > + int dw, j; > + > + msg_cdbg("Parsing JEDEC SFDP parameter table... "); > + if (len != 9 * 4 && len != 4 * 4) { > + msg_cerr("%s: len out of spec\n", __func__); > + return 1; > + } > + msg_cdbg2("\n"); > + > + /* 1. double word */ > + dw = 0; > + tmp32 = buf[(4 * dw) + 0]; > + tmp32 |= ((unsigned int)buf[(4 * dw) + 1]) << 8; > + tmp32 |= ((unsigned int)buf[(4 * dw) + 2]) << 16; > + tmp32 |= ((unsigned int)buf[(4 * dw) + 3]) << 24; > + > + 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"); > + break; > + case 0x2: > + msg_cdbg(" 4-Byte only addressing not supported.\n"); > + return 1; > + default: > + msg_cdbg(" Required addressing mode (0x%x) not supported.\n", > + tmp8); > + return 1; > + } > + > + msg_cdbg2(" Writes to the status register have "); > + if (tmp32 & (1 << 3)) { > + msg_cdbg2("to be enabled with "); > + if (tmp32 & (1 << 4)) { > + f->feature_bits = FEATURE_WRSR_WREN; > + msg_cdbg2("WREN (0x06).\n"); > + } else { > + f->feature_bits = FEATURE_WRSR_EWSR; > + msg_cdbg2("EWSR (0x50).\n"); > + } > + } else > + msg_cdbg2("not to be especially enabled.\n"); > + > + msg_cdbg2(" Write granularity is "); > + if (tmp32 & (1 << 2)) { > + msg_cdbg2("at least 64 B.\n"); > + f->write = spi_chip_write_256; > + } else { > + msg_cdbg2("1 B only.\n"); > + f->write = spi_chip_write_1; > + } > + > + if ((tmp32 & 0x3) == 0x1) { > + opcode_4k = (tmp32 >> 8) & 0xFF; /* will be dealt with later */ > + } > + > + /* 2. double word */ > + dw = 1; > + tmp32 = buf[(4 * dw) + 0]; > + tmp32 |= ((unsigned int)buf[(4 * dw) + 1]) << 8; > + tmp32 |= ((unsigned int)buf[(4 * dw) + 2]) << 16; > + tmp32 |= ((unsigned int)buf[(4 * dw) + 3]) << 24; > + > + if (tmp32 & (1 << 31)) { > + msg_cerr("Flash chip size >= 4 Gb/512 MB not supported.\n"); > + return 1; > + } > + total_size = ((tmp32 & 0x7FFFFFFF) + 1) / 8; > + f->total_size = total_size / 1024; > + msg_cdbg2(" Flash chip size is %d kB.\n", f->total_size); > + > + /* FIXME: double words 3-7 contain unused fast read information */ > + > + if (len == 4 * 4) { > + msg_cdbg("It seems like this chip supports the preliminary " > + "Intel version of SFDP, skipping processing of double " > + "words 3-9.\n"); > + goto proc_4k; > + } > + > + dw = 8; > + for (j = 0; j < 4; j++) { > + /* 8 double words from the start + 2 words for every eraser */ > + tmp32 = buf[(4 * dw) + (2 * j)]; > + if (tmp32 == 0) { > + msg_cdbg2(" Block eraser %d is unused.\n", j); > + continue; > + } > + if (tmp32 >= 31) { > + msg_cdbg2(" Block size of eraser %d (2^%d) is too big." > + "\n", j, tmp32); > + continue; > + } > + bsize = 1 << (tmp32); /* bsize = 2 ^ field */ > + > + tmp8 = buf[(4 * dw) + (2 * j) + 1]; > + if(sfdp_add_uniform_eraser(f, tmp8, bsize)) > + continue; > + /* If there is a valid 4k value in the last double words, > + * we want to override the value from double word 1, hence force > + * skipping its processing: */ > + if (bsize == 4 * 1024) > + opcode_4k = 0xFF; > + } > + > +proc_4k: > + if (opcode_4k != 0xFF) { > + sfdp_add_uniform_eraser(f, opcode_4k, 4 * 1024); > + } > + msg_cdbg("done.\n"); > + return 0; > +} > + > +static int sfdp_fetch_pt(struct flashctx *flash, uint32_t addr, uint8_t > *buf, uint16_t len) > +{ > + uint16_t i; > + if (spi_sfdp(flash, addr, buf, len)) { > + msg_cerr("Receiving SFDP parameter table failed.\n"); > + return 1; > + } > + msg_cspew(" Parameter table contents:\n"); > + for(i = 0; i < len; i++) { > + if ((i % 8) == 0) { > + msg_cspew(" 0x%03x: ", i); > + } > + msg_cspew(" 0x%02x", buf[i]); > + if ((i % 8) == 7) { > + msg_cspew("\n"); > + continue; > + } > + if ((i % 8) == 3) { > + msg_cspew(" "); > + continue; > + } > + } > + msg_cspew("\n"); > + return 0; > +} > + > +int probe_spi_sfdp(struct flashctx *flash) > +{ > + int ret = 0; > + uint8_t buf[8]; > + uint32_t tmp32; > + uint8_t nph; > + /* 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; > + > + if (spi_sfdp(flash, 0x00, buf, 4)) { > + msg_cerr("Receiving SFDP signature failed.\n"); > + return 0; > + } > + tmp32 = buf[0]; > + tmp32 |= ((unsigned int)buf[1]) << 8; > + tmp32 |= ((unsigned int)buf[2]) << 16; > + tmp32 |= ((unsigned int)buf[3]) << 24; > + > + msg_cdbg2("SFDP signature = 0x%08x (should be 0x50444653)\n", tmp32); > + if (tmp32 != 0x50444653) { > + msg_cdbg("No SFDP signature found.\n"); > + return 0; > + } > + if (spi_sfdp(flash, 0x04, buf, 3)) { > + msg_cerr("Receiving SFDP revision and number of parameter " > + "headers (NPH) failed. "); > + return 0; > + } > + msg_cdbg2("SFDP revision = %d.%d\n", buf[1], buf[0]); > + nph = buf[2]; > + msg_cdbg2("SFDP number of parameter headers (NPH) = %d (+ 1 mandatory)" > + "\n", nph); > + > + /* Fetch all parameter headers, even if we don't use them all (yet). */ > + hbuf = malloc(sizeof(struct sfdp_tbl_hdr) * (nph + 1)); > + hdrs = malloc((nph + 1) * 8); > + if (hbuf == NULL || hdrs == NULL ) { > + msg_gerr("Out of memory!\n"); > + exit(1); /* FIXME: shutdown gracefully */ > + } > + if (spi_sfdp(flash, 0x08, hbuf, (nph + 1) * 8)) { > + msg_cerr("Receiving SFDP parameter table headers failed.\n"); > + goto cleanup_hdrs; > + } > + > + i = 0; > + do { > + uint16_t len; > + hdrs[i].id = hbuf[(8 * i) + 0]; > + 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; > + msg_cdbg2("SFDP 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; > + msg_cdbg2(" Length %d B, Parameter Table Pointer 0x%06x\n", > + len, tmp32); > + > + if (len + tmp32 > UINT16_MAX) { > + msg_cerr("SFDP Parameter Table %d supposedly " > + "overflows addressable SFDP area. This most\n" > + "probably indicates a corrupt SFDP parameter " > + "table header. Aborting SFDP probe!\n", i); > + ret = 0; > + goto cleanup_hdrs; > + } > + > + tbuf = malloc(len); > + if (tbuf == NULL) { > + msg_gerr("Out of memory!\n"); > + exit(1); /* FIXME: shutdown gracefully */ > + } > + if (sfdp_fetch_pt(flash, tmp32, tbuf, len)){ > + msg_cerr("Fetching SFDP parameter table %d failed.\n", > + i); > + free(tbuf); > + break; > + } > + if (i == 0) { /* Mandatory JEDEC SFDP parameter table */ > + if (hdrs[i].id != 0) > + msg_cdbg("ID of the mandatory JEDEC SFDP " > + "parameter table is not 0 as demanded " > + "by JESD216 (warning only).\n"); > + > + if (len != 9 * 4 && len != 4 * 4) { > + msg_cdbg("Length of the mandatory JEDEC SFDP " > + "parameter table is %d B instead of " > + "36 B (i.e. 9 double words) as " > + "demanded by JESD216, skipping " > + "parsing.\n", len); > + } else if (sfdp_fill_flash(flash, tbuf, len) == 0) > + ret = 1; > + } > + > + free(tbuf); > + i++; > + } while(i <= nph); > + > +cleanup_hdrs: > + free(hdrs); > + free(hbuf); > + return ret; > +} Regards, Carl-Daniel -- http://www.hailfinger.org/ _______________________________________________ flashrom mailing list flashrom@flashrom.org http://www.flashrom.org/mailman/listinfo/flashrom