Hi,

Regarding block protection scheme, I am very glad to say I have had a breakthrough observation. After having sifted through around 90 datasheets, I was able to spot a pattern that majority of chips follow. GigaDevice and Winbond , along with some AMIC, Eon, Fudan and Macronix chips follow this pattern - around 50 in number. With this pattern, we can now dynamically generate the entire block protection ranges on-the-go. We don't need to hard-code the tables for these chips anymore and presence (or absence) of SEC, TB, or CMP bits is also handles generically! The exact algorithm is slightly convoluted, but I'll try my best to explain it. Feel free to skip past #5, you will have nothing much to lose. For all practical purposes, the BP4 and BP3 bits are identical to SEC and TB bits respectively. We'll call tuple of (BP3, BP2, BP1) as BP, and X is don't care.
1. For BP=(0,0,0):
    No protection, SEC=X, TB=X

2. For BP=(1,1,1):
    All protection, SEC=X, TB=X

3. For (SEC,TB)=(0,0):
We introduce a parameter f, such that f = chip_size(in kB) / 64 for chips_size < 4096 kB; and f = 64 for chips_size >= 4096 kB. 3.1 For chip_size >= 4096 kB, we have the following correspondence of fraction of chip_size with BP:
    (0,0,1) - 1/64
    (0,1,0) - 1/32
    (0,1,1) - 1/16
    ...
    (1,1,0) - 1/2
3.2 For chip_size = 2048 kB, we have the following correspondence of fraction of chip_size with BP:
    (0,0,1) - 1/32
    (0,1,0) - 1/16
    (0,1,1) - 1/8
    ...
    (1,1,0) - 1
3.3 For chip_size = 1024 kB, we have the following correspondence of fraction of chip_size with BP:
    (0,0,1) - 1/16
    (0,1,0) - 1/8
    (0,1,1) - 1/4
    ...
    (1,1,0) - 1
3.4 For chip_size = 512 kB, we have the following correspondence of fraction of chip_size with BP:
    (0,0,1) - 1/8
    (0,1,0) - 1/4
    ...
    (1,1,0) - 1
3.5 For chip_size = 256 kB, we have the following correspondence of fraction of chip_size with BP:
    (0,0,1) - 1/4
    (0,1,0) - 1/2
    (0,1,1) - 1
    (1,0,0) - 0
    (1,0,1) - 1/4
    (1,1,0) - 1/2
3.6 For chip_size = 128 kB, we have the following correspondence of fraction of chip_size with BP:
    (0,0,1) - 1/2
    (0,1,0) - 1
    (0,1,1) - 1
    (1,0,0) - 0
    (1,0,1) - 1/2
    (1,1,0) - 1
(SEC,TB)=(0,0) represents the upper portion of the chip (which means range addresses always end at top boundary).
E.g. For a 4096 kB chip, (0,1,0) 1/32 means upper 256 kB - 0x3fff00-0x3fffff

4. For (SEC,TB)=(0,1):
This represents the lower portion of the chip (which means range addresses always start at lower boundary). The same mapping defined earlier for fraction of chip_size holds.
E.g. For a 512 kB chip, (0,1,0) means lower 128 kB - 0x000000-0x01ffff

5. For (SEC,TB)=(1,X):
    (0,0,1) - 4 kB
    (0,1,0) - 8 kB
    (0,1,1) - 16 kB
    (1,0,0) - 32 kB
    (1,0,1) - 32 kB
   *(1,1,0) - 32 kB
We have upper for TB=0, lower for TB=1
E.g. For a 1024 kB chip TB=1, (1,0,0) means lower 32 kB - 0x000000-0x007fff
*Some chips have 64 kB block instead of 32 kB.

(You will not lose much if you skip up to here)

6. For chips with CMP bit:
For all chips that I have seen (and that is well over a 100 SPI chips), the block protection range either starts at lower boundary of chip or ends at upper boundary of chip. So, given the range and size of the chip, we can compute the complement range.

Please find attached pattern.c. It is an excerpt from the unmerged code I am working on and contains the implementation of the discussed model (cf. sec_block_pattern_range_generic). There are other functions and structs to show the models impact on the infrastructure. IMO, this is an immense improvement from what we have in ChromiumOS fork and what we have been discussing. Here are the key takeaway points - - Having the status register layout member in struct flashchip is immensely beneficial. A lot of chips can be supported with the generic code, perhaps with only slight adaptation. - Based on David's suggestion, shifting to function pointers was a good choice - it allows flexibility. - Presence (or absence) of CMP, TB (BP3), SEC (BP4) bit is automatically taken of, and the corresponding range is also dynamically generated. - For chips whose ranges cannot be dynamically generated, we have the option of specifying range manually. I think it is best to stick to range for such representation, instead of range of sectors/blocks or portions of chip (as suggested by David in the previous mail). Sticking with range is safe because range is independent of sector/block and will yield the proper result. - Based on Stefan's suggestion some time ago, having pointers to struct status_register and struct wp will allow greater re-use (which is happening a lot!)

Please have a look at the source file. The output of the source is here (http://paste.flashrom.org/view.php?id=2940)

Further work -
- In the current implementation, for chips whose range is dynamically generated we have a pointer to the range and an ID variable to associate the range with the chip (this is so that range is computed once and reused for subsequent calls). If we have another chip then the ID and pointer gets overwritten (like is happening in main() in the source). So when we want to return to the previous chip, we have to again spend the computing cost. This can be enhanced if we use list data structures and append newly computed range tables. That way, we can always revisit a previously computed table at a nominal computing cost. - The computation of BP bitfield from a given range can be enhanced if we factor in CMP bit (if present) - there could be a scenario where CMP=0 and the given range is not possible, but if CMP=1 the range could be possible. - Another way we could enhance it is through suggesting nearest neighbours to the range. (I find this idea theoretically interesting, but practically I cannot come up with use cases)

I hope you enjoyed going through the source. It took me a while to sift through so many datasheets and try out varying models, but I was elated with joy when the above algorithm was ready. I am looking forward to your feedback and comments on this.

Thanks for your time and patience.

Bye,
Hatim

/*
 * Author	: Hatim Kanchwala
 * Email	: [email protected]
 */

#include <stdio.h>
#include <stdint.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define LEN_RANGES 32

#define GIGADEVICE_GD25Q64		0x4017
#define GIGADEVICE_GD25VQ41B	0x4213
#define GIGADEVICE_GD25VQ21B	0x4212
#define GIGADEVICE_GD25VQ80C	0x4214
#define WINBOND_W25X80			0x3014
#define WINBOND_W25Q64			0x6071
#define WINBOND_EN25Q32			0x3316
#define WINBOND_EN25Q128		0x3018

// Temporary block for demonstrational purposes
#ifdef SET_CMP
#define CMP_VALUE 1
#else
#define CMP_VALUE 0
#endif
const unsigned char cmp_value = CMP_VALUE;

enum bit_state {
	X 	= -1,
	OFF = 0,
	ON 	= 1,
};

enum status_register_bit {
	INVALID_BIT = 0, RESV, WIP, WEL, SRP0, SRP1, WP, BP0, BP1, BP2, BP3, BP4,
	SEC, TB, CMP, LB3, LB2, LB1, WPS, QE, SUS1, SUS2, DRV0, DRV1, RST, HPF
};

enum wp_mode {
	WP_MODE_UNKNOWN = -1,
	WP_MODE_HARDWARE,
	WP_MODE_POWER_CYCLE,
	WP_MODE_PERMANENT,
};

enum status_register_num { SR1 = 0, SR2, SR3 };

const char *status_register_bit_name[] = {
	"", "RESV", "WIP/BUSY", "WEL", "SRP0", "SRP1", "WPDIS", "BP0", "BP1", "BP2", "BP3", "BP4", "SEC", 
	"TB", "CMP", "LB3", "LB2", "LB1", "WPS", "QE", "SUS1", "SUS2", "DRV0", "DRV1", "RST", "HPF"
};

const char *bit_string_8[8]		= { "0 0 0", "0 0 1", "0 1 0", "0 1 1", "1 0 0", "1 0 1", "1 1 0", "1 1 1" };
const char *bit_string_16[16]	= { "0 0 0 0", "0 0 0 1", "0 0 1 0", "0 0 1 1", "0 1 0 0", "0 1 0 1", "0 1 1 0", "0 1 1 1",
									"1 0 0 0", "1 0 0 1", "1 0 1 0", "1 0 1 1", "1 1 0 0", "1 1 0 1", "1 1 1 0", "1 1 1 1" };
const char *bit_string_32[32]	= { "0 0 0 0 0", "0 0 0 0 1", "0 0 0 1 0", "0 0 0 1 1", "0 0 1 0 0", "0 0 1 0 1", "0 0 1 1 0", "0 0 1 1 1",
									"0 1 0 0 0", "0 1 0 0 1", "0 1 0 1 0", "0 1 0 1 1", "0 1 1 0 0", "0 1 1 0 1", "0 1 1 1 0", "0 1 1 1 1", 
									"1 0 0 0 0", "1 0 0 0 1", "1 0 0 1 0", "1 0 0 1 1", "1 0 1 0 0", "1 0 1 0 1", "1 0 1 1 0", "1 0 1 1 1",
									"1 1 0 0 0", "1 1 0 0 1", "1 1 0 1 0", "1 1 0 1 1", "1 1 1 0 0", "1 1 1 0 1", "1 1 1 1 0", "1 1 1 1 1" };

const uint16_t sec_block_multiples_64[8]	= { 0, 1, 1, 1, 0, 1, 1, 1 };
const uint16_t sec_block_multiples_128[8] 	= { 0, 2, 1, 1, 0, 2, 1, 1 };
const uint16_t sec_block_multiples_256[8] 	= { 0, 4, 2, 1, 0, 4, 2, 1 };
const uint16_t sec_block_multiples_512[8]	= { 0, 8, 4, 2, 1, 1, 1, 1 };
const uint16_t sec_block_multiples_1024[8] 	= { 0, 16, 8, 4, 2, 1, 1, 1 };
const uint16_t sec_block_multiples_2048[8]	= { 0, 32, 16, 8, 4, 2, 1, 1 };
const uint16_t sec_block_multiples_4096[8]	= { 0, 64, 32, 16, 8, 4, 2, 1 };

struct range {
	uint32_t start;
	uint32_t len;	/* in kB */
};

struct flashchip;

struct status_register {
	struct status_register_layout {
		enum status_register_bit bits[8];
	} layout[4];

	uint8_t (*read_status)(const struct flashchip *flashchip, enum status_register_num);
	int (*write_status)(const struct flashchip *flashchip, enum status_register_num, int status);
};

struct wp {
	struct range *ranges;

	/* Return pointer to WP table (CMP=0) */
	struct range* (*ranges_table)(const struct flashchip *flashchip);

	/* Given WP bits' configuration, return the corresponding range */
	struct range* (*bp_to_range)(const struct flashchip *flashchip, unsigned char bp_config);

	/* Given WP range, return WP bits' configuration (or -1, if does not exist) */
	char (*range_to_bp)(const struct flashchip *flashchip, uint32_t start, uint32_t len);

	/* Given WP bits' configuration, set status register */
	int (*bp_to_status)(const struct flashchip *flashchip, unsigned char bp_config);

	/* Get WP mode */
	enum wp_mode (*get_wp_mode)(const struct flashchip *flashchip);

	/* Set WP mode */
	int (*set_wp_mode)(const struct flashchip *flashchip);

	/* Print WP table */
	int (*list_table)(const struct flashchip *flashchip);

	/* Get value of CMP bit from chip */
	unsigned char (*get_cmp)(const struct flashchip *flashchip);

	/* Set value of CMP bit to status */
	int (*set_cmp)(const struct flashchip *flashchip, unsigned char cmp);
};

struct flashchip {
	uint32_t model_id;
	const char *name;
	unsigned int total_size;

	struct status_register *status_register;

	struct wp *wp;
};

static uint32_t wp_table_id;
static uint32_t cmp_wp_table_id;
static struct range *wp_table;
static struct range *cmp_wp_table;

int pos_bit_generic(const struct flashchip *flashchip, enum status_register_bit bit) {
	int result = -1;

	for (int i = 0; flashchip->status_register->layout[i].bits[0] != INVALID_BIT; i++) {
		for (int j = 0; j < 8; j++) {
			if (flashchip->status_register->layout[i].bits[j] == bit) {
				result = (8 * i) + j;
				break;
			}
		}
	}

	return result;
}

struct range *sec_block_pattern_range_generic(const struct flashchip *flashchip, char cmp) {
	static struct range ranges[LEN_RANGES];

	unsigned char convention = 1;
	uint32_t top = flashchip->total_size - 1;
	uint16_t *sec_block_multiples;

	switch (flashchip->total_size) {
	case 128:
		sec_block_multiples = sec_block_multiples_128;
		break;
	case 256:
		sec_block_multiples = sec_block_multiples_256;
		break;
	case 512:
		sec_block_multiples = sec_block_multiples_512;
		break;
	case 1024:
		sec_block_multiples = sec_block_multiples_1024;
		convention = 0;
		break;
	case 2 * 1024:
		sec_block_multiples = sec_block_multiples_2048;
		convention = 0;
		break;
	case 4 * 1024:
	case 8 * 1024:
	case 16 * 1024:
		sec_block_multiples = sec_block_multiples_4096;
		break;
	}

	/* SEC, TB = (0, 0) Uppper */
	for (int i = 0; i < 8; i++) {
		if (sec_block_multiples[i]) {
			ranges[i].start = (flashchip->total_size - (flashchip->total_size / sec_block_multiples[i])) * 1024;
			ranges[i].len	= flashchip->total_size / sec_block_multiples[i];
		} else {
			ranges[i].start = 0x000000;
			ranges[i].len 	= 0;
		}
	}

	if (!(pos_bit_generic(flashchip, TB) != -1 || pos_bit_generic(flashchip, BP3) != -1))
		return ranges;

	/* SEC, TB = (0, 1) Bottom */
	for (int i = 0; i < 8; i++) {
		if (sec_block_multiples[i]) {
			ranges[(0x01 << 3) | i].start 	= 0x000000;
			ranges[(0x01 << 3) | i].len	= flashchip->total_size / sec_block_multiples[i];
		} else {
			ranges[(0x01 << 3) | i].start 	= 0x000000;
			ranges[(0x01 << 3) | i].len 	= 0;
		}
	}

	if (!(pos_bit_generic(flashchip, SEC) != -1 || pos_bit_generic(flashchip, BP4) != -1))
		return ranges;

	/* SEC, TB = (1, 0) Uppper */
	ranges[(0x02 << 3) | 0x00].start = 0x000000;
	ranges[(0x02 << 3) | 0x00].len = 0;
	ranges[(0x02 << 3) | 0x01].start = (flashchip->total_size - 4) * 1024;
	ranges[(0x02 << 3) | 0x01].len = 4;
	ranges[(0x02 << 3) | 0x02].start = (flashchip->total_size - 8) * 1024;
	ranges[(0x02 << 3) | 0x02].len = 8;
	ranges[(0x02 << 3) | 0x03].start = (flashchip->total_size - 16) * 1024;
	ranges[(0x02 << 3) | 0x03].len = 16;
	ranges[(0x02 << 3) | 0x04].start = (flashchip->total_size - 32) * 1024;
	ranges[(0x02 << 3) | 0x04].len = 32;
	ranges[(0x02 << 3) | 0x05].start = (flashchip->total_size - 32) * 1024;
	ranges[(0x02 << 3) | 0x05].len = 32;
	if (convention) {
		ranges[(0x02 << 3) | 0x06].start = (flashchip->total_size - 32) * 1024;
		ranges[(0x02 << 3) | 0x06].len = 32;
	} else {
		ranges[(0x02 << 3) | 0x06].start = 0x000000;
		ranges[(0x02 << 3) | 0x06].len = flashchip->total_size;
	}
	ranges[(0x02 << 3) | 0x07].start = 0x000000;
	ranges[(0x02 << 3) | 0x07].len = flashchip->total_size;

	/* SEC, TB = (1, 1) Lower */
	ranges[(0x03 << 3) | 0x00].start = 0x000000;
	ranges[(0x03 << 3) | 0x00].len = 0;
	ranges[(0x03 << 3) | 0x01].start = 0x000000;
	ranges[(0x03 << 3) | 0x01].len = 4;
	ranges[(0x03 << 3) | 0x02].start = 0x000000;
	ranges[(0x03 << 3) | 0x02].len = 8;
	ranges[(0x03 << 3) | 0x03].start = 0x000000;
	ranges[(0x03 << 3) | 0x03].len = 16;
	ranges[(0x03 << 3) | 0x04].start = 0x000000;
	ranges[(0x03 << 3) | 0x04].len = 32;
	ranges[(0x03 << 3) | 0x05].start = 0x000000;
	ranges[(0x03 << 3) | 0x05].len = 32;
	ranges[(0x02 << 3) | 0x06].start = 0x000000;
	if (convention) {
		ranges[(0x02 << 3) | 0x06].len = 32;
	} else {
		ranges[(0x02 << 3) | 0x06].len = flashchip->total_size;
	}
	ranges[(0x03 << 3) | 0x07].start = 0x000000;
	ranges[(0x03 << 3) | 0x07].len = flashchip->total_size;

	if (cmp == ON) {
		for (int i = 0; i < LEN_RANGES; i++) {
			if (ranges[i].len == 0) {
				ranges[i].len = flashchip->total_size;
			} else if (ranges[i].len == flashchip->total_size) {
				ranges[i].len = 0;
			} else {
				if (ranges[i].start == 0x000000)
					ranges[i].start = ranges[i].len * 1024;
				else
					ranges[i].start = 0x000000;
				ranges[i].len = flashchip->total_size - ranges[i].len;
			}
		}
	}

	return ranges;
}

unsigned char bp_bitfield_generic(const struct flashchip *flashchip) {
	unsigned char bitfield = 0x00;
	enum status_register_bit bits[] = { BP0, BP1, BP2, BP3, BP4, SEC, TB };

	for (int i = 0; i < ARRAY_SIZE(bits); i++) {
		if (pos_bit_generic(flashchip, bits[i]) != -1)
			bitfield |= 1 << pos_bit_generic(flashchip, bits[i]);
	}

	return bitfield;
}

int list_table_generic(const struct flashchip *flashchip) {
	unsigned char bitfield = bp_bitfield_generic(flashchip) >> pos_bit_generic(flashchip, BP0);
	int limit = 0;
	for (int i = 0; i < 8; i++)
		if (bitfield & (1 << i))
			limit++;
	limit = 1 << limit;

	char **bit_string = (limit == 32) ? bit_string_32 : (limit == 16) ? bit_string_16 : bit_string_8;
	struct range *table = flashchip->wp->ranges_table(flashchip);

	for (int i = 0; i < limit; i++) {
		printf("%-8s\t0x%06x\t0x%06x\n",
			bit_string[i],
			(table[i].len) ? table[i].start : 0,
			(table[i].len) ? ((table[i].len * 1024) + table[i].start - 1) : 0);
	}

	return 0;
}

struct range *bp_to_range_generic(const struct flashchip *flashchip, unsigned char bp_config) {
	return &(flashchip->wp->ranges_table(flashchip)[bp_config]);
}

char range_to_bp_generic(const struct flashchip *flashchip, uint32_t start, uint32_t len) {
	struct range *table = flashchip->wp->ranges_table(flashchip);

	for (char i = 0; i < LEN_RANGES; i++) {
		if (table[i].start == start && table[i].len == len)
			return i;
	}

	return -1;
}

unsigned char get_cmp_generic(const struct flashchip *flashchip) {
	int cmp_pos = pos_bit_generic(flashchip, CMP);
	enum status_register_num sr = cmp_pos / 8;
	uint8_t status = flashchip->status_register->read_status(flashchip, sr);
	return (status & (1 << (sr * 8))) >> (sr * 8);
};

int set_cmp_generic(const struct flashchip *flashchip, unsigned char cmp) {
	int cmp_pos = pos_bit_generic(flashchip, CMP);
	enum status_register_num sr = cmp_pos / 8;
	uint8_t status = flashchip->status_register->read_status(flashchip, sr);
	status |= 1 << (sr * 8);
	return flashchip->status_register->write_status(flashchip, sr, status);
};

struct range *table_dyn_generic(const struct flashchip *flashchip) {
	// This is a temporary arrangement, actually value of CMP will come from RDSR1/2/3
	if (cmp_value) {
		if (cmp_wp_table_id != flashchip->model_id) {
			cmp_wp_table_id = flashchip->model_id;
			cmp_wp_table = sec_block_pattern_range_generic(flashchip, 1);
		}
		return cmp_wp_table;
	} else {
		if (wp_table_id != flashchip->model_id) {
			wp_table_id = flashchip->model_id;
			wp_table = sec_block_pattern_range_generic(flashchip, 0);
		}
		return wp_table;
	}
}

struct range *w25x_table_dyn(const struct flashchip *flashchip) {
	// This is a temporary arrangement, actually value of CMP will come from RDSR1/2/3
	if (wp_table_id != flashchip->model_id) {
		wp_table_id = flashchip->model_id;
		wp_table = sec_block_pattern_range_generic(flashchip, 0);
	}
	return wp_table;
}

struct range *table_man_generic(const struct flashchip *flashchip) {
	return flashchip->wp->ranges;
}

struct status_register gd25q64_statreg = {
	.layout = {
		{ WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 },
		{ SRP1, QE, SUS2, LB1, LB2, LB3, CMP, SUS1 },
		{ RESV, RESV, RESV, RESV, HPF, DRV0, DRV1, RESV },
	},
};

struct status_register gd25vq_statreg = {
	.layout = {
		{ WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 },
		{ SRP1, QE, HPF, LB1, LB2, LB3, CMP, SUS1 },
	},
};

struct status_register gd25vq16c_statreg = {
	.layout = {
		{ WIP, WEL, BP0, BP1, BP2, BP3, BP4, SRP0 },
		{ SRP1, QE, LB1, RESV, RESV, HPF, CMP, SUS1 },
	},
};

struct status_register w25x_statreg = {
	.layout = {
		{ WIP, WEL, BP0, BP1, BP2, TB, RESV, SRP0 },
	},
};

struct status_register w25q64_statreg = {
	.layout = {
		{ WIP, WEL, BP0, BP1, BP2, TB, SEC, SRP0 },
		{ SRP1, QE, RESV, LB1, LB2, LB3, CMP, SUS1 },
	},
};

struct status_register en25q_statreg = {
	.layout = {
		{ WIP, WEL, BP0, BP1, BP2, RESV, RESV, SRP0 },
	},
};

struct status_register en25q128_statreg = {
	.layout = {
		{ WIP, WEL, BP0, BP1, BP2, BP3, WP, SRP0 },
	},
};

struct wp gd25_wp = {
	.ranges_table	= table_dyn_generic,
	.bp_to_range	= bp_to_range_generic,
	.range_to_bp	= range_to_bp_generic,
	.list_table		= list_table_generic,
	.get_cmp		= get_cmp_generic,
	.set_cmp		= set_cmp_generic,
};

struct wp w25x_wp = {
	.ranges_table	= w25x_table_dyn,
	.bp_to_range	= bp_to_range_generic,
	.range_to_bp	= range_to_bp_generic,
	.list_table		= list_table_generic,
	.get_cmp		= get_cmp_generic,
	.set_cmp		= set_cmp_generic,
};

struct range en25q128_ranges[LEN_RANGES] = {
	{ 0x000000, 0 },
	{ 0x000000, 16320 },
	{ 0x000000, 16256 },
	{ 0x000000, 16128 },
	{ 0x000000, 15872 },
	{ 0x000000, 15360 },
	{ 0x000000, 14336 },
	{ 0x000000, 16384 },
	{ 0x000000, 0 },
	{ 0x010000, 16320 },
	{ 0x020000, 16256 },
	{ 0x040000, 16128 },
	{ 0x080000, 15872 },
	{ 0x100000, 15360 },
	{ 0x200000, 14336 },
	{ 0x000000, 16384 },
};

struct wp en25q128_wp = {
	.ranges			= en25q128_ranges,
	.ranges_table	= table_man_generic,
	.bp_to_range	= bp_to_range_generic,
	.range_to_bp	= range_to_bp_generic,
	.list_table		= list_table_generic,
};

const struct flashchip gd25q64 = {
	.model_id = GIGADEVICE_GD25Q64,
	.name = "GIGADEVICE_GD25Q64",
	.total_size	= 8 * 1024,
	.status_register = &gd25q64_statreg,
	.wp = &gd25_wp,	
};

const struct flashchip gd25vq41b = {
	.model_id = GIGADEVICE_GD25VQ41B,
	.name = "GIGADEVICE_GD25VQ41B",
	.total_size	= 512,
	.status_register = &gd25vq_statreg,
	.wp = &gd25_wp,	
};

const struct flashchip gd25vq21b = {
	.model_id = GIGADEVICE_GD25VQ21B,
	.name = "GIGADEVICE_GD25VQ21B",
	.total_size	= 256,
	.status_register = &gd25vq_statreg,
	.wp = &gd25_wp,	
};

const struct flashchip gd25vq16c = {
	.model_id = GIGADEVICE_GD25VQ21B,
	.name = "GIGADEVICE_GD25VQ21B",
	.total_size	= 2 * 1024,
	.status_register = &gd25vq16c_statreg,
	.wp = &gd25_wp,	
};

const struct flashchip gd25vq80c = {
	.model_id = GIGADEVICE_GD25VQ80C,
	.name = "GIGADEVICE_GD25VQ80C",
	.total_size	= 1024,
	.status_register = &gd25vq16c_statreg,
	.wp = &gd25_wp,	
};

const struct flashchip w25x80 = {
	.model_id = WINBOND_W25X80,
	.name = "WINBOND_W25X80",
	.total_size	= 1024,
	.status_register = &w25x_statreg,
	.wp = &w25x_wp,
};

const struct flashchip w25q64 = {
	.model_id = WINBOND_W25Q64,
	.name = "WINBOND_W25Q64",
	.total_size	= 8 * 1024,
	.status_register = &w25q64_statreg,
	.wp = &gd25_wp,
};

const struct flashchip en25q32 = {
	.model_id = WINBOND_EN25Q32,
	.name = "WINBOND_EN25Q32",
	.total_size	= 4 * 1024,
	.status_register = &en25q_statreg,
	.wp = &gd25_wp,
};

const struct flashchip en25q128 = {
	.model_id = WINBOND_EN25Q128,
	.name = "WINBOND_EN25Q128",
	.total_size	= 16 * 1024,
	.status_register = &en25q128_statreg,
	.wp = &en25q128_wp,
};

void test(const struct flashchip *flashchip) {
	printf("%s\n", flashchip->name);
	flashchip->wp->list_table(flashchip);
	printf("{ 0x%06x, %2d }\n", (flashchip->wp->bp_to_range(flashchip, 0x06))->start, (flashchip->wp->bp_to_range(flashchip, 0x06))->len);
	printf("%s\n", (flashchip->wp->range_to_bp(flashchip, 0x780000, 512) == -1) ? "UNKNOWN_RANGE" : bit_string_32[flashchip->wp->range_to_bp(flashchip, 0x780000, 512)]);
	printf("\n");
}

int main(int argc, const char **argv) {
	test(&gd25vq16c);
	test(&gd25vq80c);
	test(&gd25vq21b);
	test(&gd25vq41b);
	test(&gd25q64);
	test(&w25x80);
	test(&w25q64);
	test(&en25q32);
	test(&en25q128);

	return 0;
}
_______________________________________________
flashrom mailing list
[email protected]
https://www.flashrom.org/mailman/listinfo/flashrom

Reply via email to