[flashrom] [PATCH] Add support for SFDP (JESD216).

Carl-Daniel Hailfinger c-d.hailfinger.devel.2006 at gmx.net
Wed Feb 1 00:03:34 CET 2012


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 <dhendrix at google.com>
> on W25Q64CV
>
> Signed-off-by: Stefan Tauner <stefan.tauner at 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 at 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 at 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/





More information about the flashrom mailing list