drivers/spi/spi_flash: organize spi flash by sector topology

By grouping the spi flash parts by their {vendor, sector topology}
tuple one can use a common probe function for looking up the part
instead of having per-vendor probe functions. Additionally, by
grouping by the command set one can save more space as well. SST
is the exception that requires after_probe() function to unlock the
parts.

2KiB of savings in each of verstage, romstage, and ramstage
on Aleena Chrome OS Build.

Change-Id: I9cc20ca0f3d0a1b97154b000c95ff2e7e87f3375
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/38379
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/src/drivers/spi/adesto.c b/src/drivers/spi/adesto.c
index cfcde34..89e33f78 100644
--- a/src/drivers/spi/adesto.c
+++ b/src/drivers/spi/adesto.c
@@ -44,113 +44,70 @@
 		.id			= 0x4218,
 		.name			= "AT25SL128A",
 		.nr_sectors_shift	= 12,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4501,
 		.name			= "AT25DF081A", /* Yes, 81A id < 81 */
 		.nr_sectors_shift	= 8,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4502,
 		.name			= "AT25DF081",
 		.nr_sectors_shift	= 8,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4602,
 		.name			= "AT25DF161",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4603,
 		.name			= "AT25DL161",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4700,
 		.name			= "AT25DF321",
 		.nr_sectors_shift	= 10,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4701,
 		.name			= "AT25DF321A",
 		.nr_sectors_shift	= 10,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4800,
 		.name			= "AT25DF641",
 		.nr_sectors_shift	= 11,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x8501,
 		.name			= "AT25SF081",
 		.nr_sectors_shift	= 8,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x8600,
 		.name			= "AT25DQ161",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x8601,
 		.name			= "AT25SF161",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x8700,
 		.name			= "AT25DQ321",
 		.nr_sectors_shift	= 10,
-		.sector_size_kib_shift	= 2,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
+const struct spi_flash_vendor_info spi_flash_adesto_vi = {
+	.id = VENDOR_ID_ADESTO,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0x20_sector_desc,
 };
-
-int spi_flash_probe_adesto(const struct spi_slave *spi, u8 *idcode,
-			   struct spi_flash *flash)
-{
-	const struct spi_flash_part_id *params;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
-		params = &flash_table[i];
-		if (params->id == ((idcode[1] << 8) | idcode[2]))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(flash_table)) {
-		printk(BIOS_WARNING, "SF: Unsupported adesto ID %02x%02x\n",
-				idcode[1], idcode[2]);
-		return -1;
-	}
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-	/* Assuming power-of-two page size initially. */
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_AT25DF_SE;
-	flash->status_cmd = CMD_AT25DF_RDSR;
-	flash->pp_cmd = CMD_AT25DF_PP;
-	flash->wren_cmd = CMD_AT25DF_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
diff --git a/src/drivers/spi/amic.c b/src/drivers/spi/amic.c
index fb5bdea..8e25cd9 100644
--- a/src/drivers/spi/amic.c
+++ b/src/drivers/spi/amic.c
@@ -39,96 +39,55 @@
 		.id			= 0x2015,
 		.name			= "A25L16PU",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x2025,
 		.name			= "A25L16PT",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x3014,
 		.name			= "A25L080",
 		.nr_sectors_shift	= 8,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x3015,
 		.name			= "A25L016",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x3016,
 		.name			= "A25L032",
 		.nr_sectors_shift	= 10,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4014,
 		.name			= "A25LQ080",
 		.nr_sectors_shift	= 8,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4015,
 		.name			= "A25LQ16",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4016,
 		.name			= "A25LQ032",
 		.nr_sectors_shift	= 10,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4017,
 		.name			= "A25LQ64",
 		.nr_sectors_shift	= 11,
-		.sector_size_kib_shift	= 2,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
+const struct spi_flash_vendor_info spi_flash_amic_vi = {
+	.id = VENDOR_ID_AMIC,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0x20_sector_desc,
 };
-
-int spi_flash_probe_amic(const struct spi_slave *spi, u8 *idcode,
-			 struct spi_flash *flash)
-{
-	const struct spi_flash_part_id *params;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
-		params = &flash_table[i];
-		if (params->id == ((idcode[1] << 8) | idcode[2]))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(flash_table)) {
-		printk(BIOS_WARNING, "SF: Unsupported AMIC ID %02x%02x\n",
-				idcode[1], idcode[2]);
-		return -1;
-	}
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-
-	/* Assuming power-of-two page size initially. */
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_A25_SE;
-	flash->status_cmd = CMD_A25_RDSR;
-	flash->pp_cmd = CMD_A25_PP;
-	flash->wren_cmd = CMD_A25_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
diff --git a/src/drivers/spi/atmel.c b/src/drivers/spi/atmel.c
index a494844..ae282c3 100644
--- a/src/drivers/spi/atmel.c
+++ b/src/drivers/spi/atmel.c
@@ -39,84 +39,45 @@
 		.id			= 0x3015,
 		.name			= "AT25X16",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x47,
 		.name			= "AT25DF32",
 		.nr_sectors_shift	= 10,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x3017,
 		.name			= "AT25X64",
 		.nr_sectors_shift	= 11,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4015,
 		.name			= "AT25Q16",
 		.nr_sectors_shift	= 9,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4016,
 		.name			= "AT25Q32",
 		.nr_sectors_shift	= 10,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4017,
 		.name			= "AT25Q64",
 		.nr_sectors_shift	= 11,
-		.sector_size_kib_shift	= 2,
 	},
 	{
 		.id			= 0x4018,
 		.name			= "AT25Q128",
 		.nr_sectors_shift	= 12,
-		.sector_size_kib_shift	= 2,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
+const struct spi_flash_vendor_info spi_flash_atmel_vi = {
+	.id = VENDOR_ID_ATMEL,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0x20_sector_desc,
 };
-
-int spi_flash_probe_atmel(const struct spi_slave *spi, u8 *idcode,
-			  struct spi_flash *flash)
-{
-	const struct spi_flash_part_id *params;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
-		params = &flash_table[i];
-		if (params->id == ((idcode[1] << 8) | idcode[2]))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(flash_table)) {
-		printk(BIOS_WARNING, "SF: Unsupported Atmel ID %02x%02x\n",
-				idcode[1], idcode[2]);
-		return -1;
-	}
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-
-	/* Assuming power-of-two page size initially. */
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_AT25_SE;
-	flash->status_cmd = CMD_AT25_RDSR;
-	flash->pp_cmd = CMD_AT25_PP;
-	flash->wren_cmd = CMD_AT25_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
diff --git a/src/drivers/spi/eon.c b/src/drivers/spi/eon.c
index 3270c39..6d4833c 100644
--- a/src/drivers/spi/eon.c
+++ b/src/drivers/spi/eon.c
@@ -60,167 +60,115 @@
 		.id = EON_ID_EN25B80,
 		.name = "EN25B80",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25B16,
 		.name = "EN25B16",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25B32,
 		.name = "EN25B32",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25B64,
 		.name = "EN25B64",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25F80,
 		.name = "EN25F80",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25F16,
 		.name = "EN25F16",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25F32,
 		.name = "EN25F32",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25F64,
 		.name = "EN25F64",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25Q80,
 		.name = "EN25Q80(A)",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25Q16,
 		.name = "EN25Q16(D16)",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25Q32,
 		.name = "EN25Q32(A/B)",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25Q64,
 		.name = "EN25Q64",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25Q128,
 		.name = "EN25Q128",
 		.nr_sectors_shift = 12,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25QH16,
 		.name = "EN25QH16",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25QH32,
 		.name = "EN25QH32",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25QH64,
 		.name = "EN25QH64",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25QH128,
 		.name = "EN25QH128",
 		.nr_sectors_shift = 12,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25S80,
 		.name = "EN25S80",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25S16,
 		.name = "EN25S16",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25S32,
 		.name = "EN25S32",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = EON_ID_EN25S64,
 		.name = "EN25S64",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
+const struct spi_flash_vendor_info spi_flash_eon_vi = {
+	.id = VENDOR_ID_EON,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0x20_sector_desc,
 };
-
-int spi_flash_probe_eon(const struct spi_slave *spi, u8 *idcode,
-			struct spi_flash *flash)
-{
-	const struct spi_flash_part_id *params;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); ++i) {
-		params = &flash_table[i];
-		if (params->id == ((idcode[1] << 8) | idcode[2]))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(flash_table)) {
-		printk(BIOS_WARNING, "SF: Unsupported EON ID %#02x%02x\n",
-		       idcode[1], idcode[2]);
-		return -1;
-	}
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-
-	flash->name = params->name;
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_EN25_SE;
-	flash->status_cmd = CMD_EN25_RDSR;
-	flash->pp_cmd = CMD_EN25_PP;
-	flash->wren_cmd = CMD_EN25_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
diff --git a/src/drivers/spi/gigadevice.c b/src/drivers/spi/gigadevice.c
index 010fb45..64d9706 100644
--- a/src/drivers/spi/gigadevice.c
+++ b/src/drivers/spi/gigadevice.c
@@ -39,133 +39,87 @@
 		.id				= 0x3114,
 		.name				= "GD25T80",
 		.nr_sectors_shift		= 8,
-		.sector_size_kib_shift		= 2,
 	},
 	{
 		.id				= 0x4014,
 		.name				= "GD25Q80",
 		.nr_sectors_shift		= 8,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},					/* also GD25Q80B */
 	{
 		.id				= 0x4015,
 		.name				= "GD25Q16",
 		.nr_sectors_shift		= 9,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},					/* also GD25Q16B */
 	{
 		.id				= 0x4016,
 		.name				= "GD25Q32B",
 		.nr_sectors_shift		= 10,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},					/* also GD25Q32B */
 	{
 		.id				= 0x4017,
 		.name				= "GD25Q64",
 		.nr_sectors_shift		= 11,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},					/* also GD25Q64B, GD25B64C */
 	{
 		.id				= 0x4018,
 		.name				= "GD25Q128",
 		.nr_sectors_shift		= 12,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},					/* also GD25Q128B */
 	{
 		.id				= 0x4214,
 		.name				= "GD25VQ80C",
 		.nr_sectors_shift		= 8,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x4215,
 		.name				= "GD25VQ16C",
 		.nr_sectors_shift		= 9,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x6014,
 		.name				= "GD25LQ80",
 		.nr_sectors_shift		= 8,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x6015,
 		.name				= "GD25LQ16",
 		.nr_sectors_shift		= 9,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x6016,
 		.name				= "GD25LQ32",
 		.nr_sectors_shift		= 10,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x6017,
 		.name				= "GD25LQ64C",
 		.nr_sectors_shift		= 11,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},					/* also GD25LB64C */
 	{
 		.id				= 0x6018,
 		.name				= "GD25LQ128",
 		.nr_sectors_shift		= 12,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
+const struct spi_flash_vendor_info spi_flash_gigadevice_vi = {
+	.id = VENDOR_ID_GIGADEVICE,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0x20_sector_desc,
 };
-
-int spi_flash_probe_gigadevice(const struct spi_slave *spi, u8 *idcode,
-				struct spi_flash *flash)
-{
-	const struct spi_flash_part_id *params;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
-		params = &flash_table[i];
-		if (params->id == ((idcode[1] << 8) | idcode[2]))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(flash_table)) {
-		printk(BIOS_WARNING,
-		       "SF gigadevice.c: Unsupported ID %#02x%02x\n",
-		       idcode[1], idcode[2]);
-		return -1;
-	}
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-
-	/* Assuming power-of-two page size initially. */
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_GD25_SE;
-	flash->status_cmd = CMD_GD25_RDSR;
-	flash->pp_cmd = CMD_GD25_PP;
-	flash->wren_cmd = CMD_GD25_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
diff --git a/src/drivers/spi/macronix.c b/src/drivers/spi/macronix.c
index c7a4ce0..f23b421 100644
--- a/src/drivers/spi/macronix.c
+++ b/src/drivers/spi/macronix.c
@@ -41,154 +41,105 @@
 		.id = 0x2014,
 		.name = "MX25L8005",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2015,
 		.name = "MX25L1605D",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2016,
 		.name = "MX25L3205D",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2017,
 		.name = "MX25L6405D",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2018,
 		.name = "MX25L12805D",
 		.nr_sectors_shift = 12,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2019,
 		.name = "MX25L25635F",
 		.nr_sectors_shift = 13,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x201a,
 		.name = "MX66L51235F",
 		.nr_sectors_shift = 14,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2415,
 		.name = "MX25L1635D",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2515,
 		.name = "MX25L1635E",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2534,
 		.name = "MX25U8032E",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2535,
 		.name = "MX25U1635E",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2536,
 		.name = "MX25U3235E",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2537,
 		.name = "MX25U6435F",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2538,
 		.name = "MX25U12835F",
 		.nr_sectors_shift = 12,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2539,
 		.name = "MX25U25635F",
 		.nr_sectors_shift = 13,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x253a,
 		.name = "MX25U51245G",
 		.nr_sectors_shift = 14,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x2618,
 		.name = "MX25L12855E",
 		.nr_sectors_shift = 12,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x5e16,
 		.name = "MX25L3235D", /* MX25L3225D/MX25L3236D/MX25L3237D */
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = 0x9517,
 		.name = "MX25L6495F",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
+const struct spi_flash_vendor_info spi_flash_macronix_vi = {
+	.id = VENDOR_ID_MACRONIX,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0x20_sector_desc,
 };
-
-int spi_flash_probe_macronix(const struct spi_slave *spi, u8 *idcode,
-			     struct spi_flash *flash)
-{
-	const struct spi_flash_part_id *params;
-	unsigned int i;
-	u16 id = idcode[2] | idcode[1] << 8;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
-		params = &flash_table[i];
-		if (params->id == id)
-			break;
-	}
-
-	if (i == ARRAY_SIZE(flash_table)) {
-		printk(BIOS_WARNING, "SF: Unsupported Macronix ID %04x\n", id);
-		return -1;
-	}
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * params->nr_sectors_shift;
-	flash->erase_cmd = CMD_MX25XX_SE;
-	flash->status_cmd = CMD_MX25XX_RDSR;
-	flash->pp_cmd = CMD_MX25XX_PP;
-	flash->wren_cmd = CMD_MX25XX_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
diff --git a/src/drivers/spi/spansion.c b/src/drivers/spi/spansion.c
index b4fca4f..c694f45 100644
--- a/src/drivers/spi/spansion.c
+++ b/src/drivers/spi/spansion.c
@@ -54,55 +54,49 @@
 		.id = SPSN_ID_S25FL008A,
 		.name = "S25FL008A",
 		.nr_sectors_shift = 4,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = SPSN_ID_S25FL016A,
 		.name = "S25FL016A",
 		.nr_sectors_shift = 5,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = SPSN_ID_S25FL032A,
 		.name = "S25FL032A",
 		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = SPSN_ID_S25FL064A,
 		.name = "S25FL064A",
 		.nr_sectors_shift = 7,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = (SPSN_EXT_ID_S25FL128P_64KB << 16) | SPSN_ID_S25FL128P,
 		.name = "S25FL128P_64K",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 6,
-	},
-	{
-		.id = (SPSN_EXT_ID_S25FL128P_256KB << 16) | SPSN_ID_S25FL128P,
-		.name = "S25FL128P_256K",
-		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 8,
 	},
 	{
 		.id = (SPSN_EXT_ID_S25FLXXS_64KB << 16) | SPSN_ID_S25FL128S,
 		.name = "S25FL128S_256K",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = (SPSN_EXT_ID_S25FL032P << 16) | SPSN_ID_S25FL032A,
 		.name = "S25FL032P",
 		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = (SPSN_EXT_ID_S25FLXXS_64KB << 16) | SPSN_ID_S25FL128P,
 		.name = "S25FS128S",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 6,
+	},
+};
+
+static const struct spi_flash_part_id flash_table_256k_sector[] = {
+	{
+		.id = (SPSN_EXT_ID_S25FL128P_256KB << 16) | SPSN_ID_S25FL128P,
+		.name = "S25FL128P_256K",
+		.nr_sectors_shift = 6,
 	},
 };
 
@@ -111,82 +105,50 @@
 		.id = SPSN_ID_S25FL208K,
 		.name = "S25FL208K",
 		.nr_sectors_shift = 4,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = SPSN_ID_S25FL116K,
 		.name = "S25FL116K_16M",
 		.nr_sectors_shift = 5,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = SPSN_ID_S25FL132K,
 		.name = "S25FL132K",
 		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = SPSN_ID_S25FL164K,
 		.name = "S25FL164K",
 		.nr_sectors_shift = 7,
-		.sector_size_kib_shift = 6,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
+const struct spi_flash_vendor_info spi_flash_spansion_ext1_vi = {
+	.id = VENDOR_ID_SPANSION,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 6,
+	.match_id_mask = 0xffffffff,
+	.ids = flash_table_ext,
+	.nr_part_ids = ARRAY_SIZE(flash_table_ext),
+	.desc = &spi_flash_pp_0xd8_sector_desc,
 };
 
+const struct spi_flash_vendor_info spi_flash_spansion_ext2_vi = {
+	.id = VENDOR_ID_SPANSION,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 8,
+	.match_id_mask = 0xffffffff,
+	.ids = flash_table_256k_sector,
+	.nr_part_ids = ARRAY_SIZE(flash_table_256k_sector),
+	.desc = &spi_flash_pp_0xd8_sector_desc,
+};
 
-static int match_table(const struct spi_slave *spi, struct spi_flash *flash, u32 id,
-			const struct spi_flash_part_id *parts, size_t num_parts)
-{
-	const struct spi_flash_part_id *params = NULL;
-	unsigned int i;
-
-	for (i = 0; i < num_parts; i++) {
-		if (parts[i].id != id)
-			continue;
-		params = &parts[i];
-		break;
-	}
-
-	if (params == NULL)
-		return -1;
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_S25FLXX_SE;
-	flash->status_cmd = CMD_S25FLXX_RDSR;
-	flash->pp_cmd = CMD_S25FLXX_PP;
-	flash->wren_cmd = CMD_S25FLXX_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
-
-int spi_flash_probe_spansion(const struct spi_slave *spi, u8 *idcode,
-				struct spi_flash *flash)
-{
-	u32 id;
-
-	id = ((idcode[3] << 8) | idcode[4]) << 16;
-	id |= (idcode[1] << 8) | idcode[2];
-
-	if (!match_table(spi, flash, id, flash_table_ext, ARRAY_SIZE(flash_table_ext)))
-		return 0;
-
-	if (!match_table(spi, flash, id & 0xffff, flash_table, ARRAY_SIZE(flash_table)))
-		return 0;
-
-	printk(BIOS_WARNING, "SF: Unsupported SPANSION ID %08x\n", id);
-
-	return -1;
-}
+const struct spi_flash_vendor_info spi_flash_spansion_vi = {
+	.id = VENDOR_ID_SPANSION,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 6,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0xd8_sector_desc,
+};
diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c
index 0024e9b..90dd587 100644
--- a/src/drivers/spi/spi_flash.c
+++ b/src/drivers/spi/spi_flash.c
@@ -309,64 +309,122 @@
 	return ret;
 }
 
-/*
- * The following table holds all device probe functions
- *
- * idcode: the expected IDCODE
- * probe:  the function to call
- *
- * Several matching entries are permitted, they will be tried
- * in sequence until a probe function returns non NULL.
- *
- * Probe functions will be given the idcode buffer starting at their
- * manu id byte (the "idcode" in the table below).
- */
-static struct {
-	const u8 idcode;
-	int (*probe) (const struct spi_slave *spi, u8 *idcode,
-		      struct spi_flash *flash);
-} flashes[] = {
-	/* Keep it sorted by define name */
+static const struct spi_flash_vendor_info *spi_flash_vendors[] = {
+#if CONFIG(SPI_FLASH_ADESTO)
+	&spi_flash_adesto_vi,
+#endif
 #if CONFIG(SPI_FLASH_AMIC)
-	{ VENDOR_ID_AMIC, spi_flash_probe_amic, },
+	&spi_flash_amic_vi,
 #endif
 #if CONFIG(SPI_FLASH_ATMEL)
-	{ VENDOR_ID_ATMEL, spi_flash_probe_atmel, },
+	&spi_flash_atmel_vi,
 #endif
 #if CONFIG(SPI_FLASH_EON)
-	{ VENDOR_ID_EON, spi_flash_probe_eon, },
+	&spi_flash_eon_vi,
 #endif
 #if CONFIG(SPI_FLASH_GIGADEVICE)
-	{ VENDOR_ID_GIGADEVICE, spi_flash_probe_gigadevice, },
+	&spi_flash_gigadevice_vi,
 #endif
 #if CONFIG(SPI_FLASH_MACRONIX)
-	{ VENDOR_ID_MACRONIX, spi_flash_probe_macronix, },
+	&spi_flash_macronix_vi,
 #endif
 #if CONFIG(SPI_FLASH_SPANSION)
-	{ VENDOR_ID_SPANSION, spi_flash_probe_spansion, },
+	&spi_flash_spansion_ext1_vi,
+	&spi_flash_spansion_ext2_vi,
+	&spi_flash_spansion_vi,
 #endif
 #if CONFIG(SPI_FLASH_SST)
-	{ VENDOR_ID_SST, spi_flash_probe_sst, },
+	&spi_flash_sst_ai_vi,
+	&spi_flash_sst_vi,
 #endif
 #if CONFIG(SPI_FLASH_STMICRO)
-	{ VENDOR_ID_STMICRO, spi_flash_probe_stmicro, },
+	&spi_flash_stmicro1_vi,
+	&spi_flash_stmicro2_vi,
+	&spi_flash_stmicro3_vi,
+	&spi_flash_stmicro4_vi,
 #endif
 #if CONFIG(SPI_FLASH_WINBOND)
-	{ VENDOR_ID_WINBOND, spi_flash_probe_winbond, },
-#endif
-	/* Keep it sorted by best detection */
-#if CONFIG(SPI_FLASH_ADESTO)
-	{ VENDOR_ID_ADESTO, spi_flash_probe_adesto, },
+	&spi_flash_winbond_vi,
 #endif
 };
 #define IDCODE_LEN 5
 
+static int fill_spi_flash(const struct spi_slave *spi, struct spi_flash *flash,
+	const struct spi_flash_vendor_info *vi,
+	const struct spi_flash_part_id *part)
+{
+	memcpy(&flash->spi, spi, sizeof(*spi));
+	flash->vendor = vi->id;
+	flash->model = part->id;
+	flash->name = part->name;
+
+	flash->page_size = 1U << vi->page_size_shift;
+	flash->sector_size = (1U << vi->sector_size_kib_shift) * KiB;
+	flash->size = flash->sector_size * (1U << part->nr_sectors_shift);
+	flash->erase_cmd = vi->desc->erase_cmd;
+	flash->status_cmd = vi->desc->status_cmd;
+	flash->pp_cmd = vi->desc->pp_cmd;
+	flash->wren_cmd = vi->desc->wren_cmd;
+
+	flash->flags.dual_spi = part->fast_read_dual_output_support;
+
+	flash->ops = &vi->desc->ops;
+	flash->prot_ops = vi->prot_ops;
+	flash->part = part;
+
+	if (vi->after_probe)
+		return vi->after_probe(flash);
+
+	return 0;
+}
+
+static const struct spi_flash_part_id *find_part(const struct spi_flash_vendor_info *vi,
+						uint32_t id)
+{
+	size_t i;
+
+	for (i = 0; i < vi->nr_part_ids; i++) {
+		const struct spi_flash_part_id *part = &vi->ids[i];
+
+		if (part->id == id)
+			return part;
+	}
+
+	return NULL;
+}
+
+static int find_match(const struct spi_slave *spi, struct spi_flash *flash,
+			uint8_t manuf_id, uint32_t id)
+{
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(spi_flash_vendors); i++) {
+		const struct spi_flash_vendor_info *vi;
+		const struct spi_flash_part_id *part;
+
+		vi = spi_flash_vendors[i];
+
+		if (manuf_id != vi->id)
+			continue;
+
+		part = find_part(vi, id & vi->match_id_mask);
+
+		if (part == NULL)
+			continue;
+
+		return fill_spi_flash(spi, flash, vi, part);
+	}
+
+	return -1;
+}
+
 int spi_flash_generic_probe(const struct spi_slave *spi,
 				struct spi_flash *flash)
 {
 	int ret, i;
 	u8 idcode[IDCODE_LEN];
 	u8 manuf_id;
+	u32 id;
 
 	/* Read the ID codes */
 	ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode));
@@ -392,19 +450,9 @@
 		manuf_id = idcode[0];
 	}
 
-	/* search the table for matches in shift and id */
-	for (i = 0; i < (int)ARRAY_SIZE(flashes); ++i)
-		if (flashes[i].idcode == manuf_id) {
-			/* we have a match, call probe */
-			if (flashes[i].probe(spi, idcode, flash) == 0) {
-				flash->vendor = idcode[0];
-				flash->model = (idcode[1] << 8) | idcode[2];
-				return 0;
-			}
-		}
+	id = (idcode[3] << 24) | (idcode[4] << 16) | (idcode[1] << 8) | idcode[2];
 
-	/* No match, return error. */
-	return -1;
+	return find_match(spi, flash, manuf_id, id);
 }
 
 int spi_flash_probe(unsigned int bus, unsigned int cs, struct spi_flash *flash)
@@ -701,3 +749,29 @@
 
 	return ret;
 }
+
+const struct spi_flash_ops_descriptor spi_flash_pp_0x20_sector_desc = {
+	.erase_cmd = 0x20, /* Sector Erase */
+	.status_cmd = 0x05, /* Read Status */
+	.pp_cmd = 0x02, /* Page Program */
+	.wren_cmd = 0x06, /* Write Enable */
+	.ops = {
+		.read = spi_flash_cmd_read,
+		.write = spi_flash_cmd_write_page_program,
+		.erase = spi_flash_cmd_erase,
+		.status = spi_flash_cmd_status,
+	},
+};
+
+const struct spi_flash_ops_descriptor spi_flash_pp_0xd8_sector_desc = {
+	.erase_cmd = 0xd8, /* Sector Erase */
+	.status_cmd = 0x05, /* Read Status */
+	.pp_cmd = 0x02, /* Page Program */
+	.wren_cmd = 0x06, /* Write Enable */
+	.ops = {
+		.read = spi_flash_cmd_read,
+		.write = spi_flash_cmd_write_page_program,
+		.erase = spi_flash_cmd_erase,
+		.status = spi_flash_cmd_status,
+	},
+};
diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h
index cc258de..bd52d66 100644
--- a/src/drivers/spi/spi_flash_internal.h
+++ b/src/drivers/spi/spi_flash_internal.h
@@ -69,42 +69,70 @@
 /* Read len bytes into buf at offset. */
 int spi_flash_cmd_read(const struct spi_flash *flash, u32 offset, size_t len, void *buf);
 
-/* Manufacturer-specific probe functions */
-int spi_flash_probe_spansion(const struct spi_slave *spi, u8 *idcode,
-			     struct spi_flash *flash);
-int spi_flash_probe_amic(const struct spi_slave *spi, u8 *idcode,
-			 struct spi_flash *flash);
-int spi_flash_probe_atmel(const struct spi_slave *spi, u8 *idcode,
-			  struct spi_flash *flash);
-int spi_flash_probe_eon(const struct spi_slave *spi, u8 *idcode,
-			struct spi_flash *flash);
-int spi_flash_probe_macronix(const struct spi_slave *spi, u8 *idcode,
-			     struct spi_flash *flash);
-int spi_flash_probe_sst(const struct spi_slave *spi, u8 *idcode,
-			struct spi_flash *flash);
-int spi_flash_probe_stmicro(const struct spi_slave *spi, u8 *idcode,
-			    struct spi_flash *flash);
 /* Release from deep sleep an provide alternative rdid information. */
 int stmicro_release_deep_sleep_identify(const struct spi_slave *spi, u8 *idcode);
-int spi_flash_probe_winbond(const struct spi_slave *spi, u8 *idcode,
-			    struct spi_flash *flash);
-int spi_flash_probe_gigadevice(const struct spi_slave *spi, u8 *idcode,
-			       struct spi_flash *flash);
-int spi_flash_probe_adesto(const struct spi_slave *spi, u8 *idcode,
-			   struct spi_flash *flash);
 
 struct spi_flash_part_id {
+	/* rdid command constructs a 32-bit id using the following method
+	 * for matching: 31 | id[3] | id[4] | id[1] | id[2] | 0 */
 	uint32_t id;
 	const char *name;
 	/* Log based 2 total number of sectors. */
 	uint16_t nr_sectors_shift: 4;
-	/* Log based 2 sector size */
-	uint16_t sector_size_kib_shift: 4;
 	uint16_t fast_read_dual_output_support : 1;
-	uint16_t _reserved_for_flags: 7;
+	uint16_t _reserved_for_flags: 3;
 	/* Block protection. Currently used by Winbond. */
 	uint16_t protection_granularity_shift : 5;
 	uint16_t bp_bits : 3;
 };
 
+struct spi_flash_ops_descriptor {
+	uint8_t erase_cmd; /* Sector Erase */
+	uint8_t status_cmd; /* Read Status Register */
+	uint8_t pp_cmd; /* Page program command, if supported. */
+	uint8_t wren_cmd; /* Write Enable command. */
+	struct spi_flash_ops ops;
+};
+
+/* Vendor info represents a common set of organization and commands by a given
+ * vendor. One can implement multiple sets from a single vendor by having
+ * separate objects. */
+struct spi_flash_vendor_info {
+	uint8_t id;
+	uint8_t page_size_shift : 4; /* if page programming oriented. */
+	/* Log based 2 sector size */
+	uint8_t sector_size_kib_shift : 4;
+	uint16_t nr_part_ids;
+	const struct spi_flash_part_id *ids;
+	uint32_t match_id_mask; /* matching bytes of the id for this set*/
+	const struct spi_flash_ops_descriptor *desc;
+	const struct spi_flash_protection_ops *prot_ops;
+	/* Returns 0 on success. !0 otherwise. */
+	int (*after_probe)(const struct spi_flash *flash);
+};
+
+/* Manufacturer-specific probe information */
+extern const struct spi_flash_vendor_info spi_flash_adesto_vi;
+extern const struct spi_flash_vendor_info spi_flash_amic_vi;
+extern const struct spi_flash_vendor_info spi_flash_atmel_vi;
+extern const struct spi_flash_vendor_info spi_flash_eon_vi;
+extern const struct spi_flash_vendor_info spi_flash_gigadevice_vi;
+extern const struct spi_flash_vendor_info spi_flash_macronix_vi;
+/* Probing order matters between the spansion sequence. */
+extern const struct spi_flash_vendor_info spi_flash_spansion_ext1_vi;
+extern const struct spi_flash_vendor_info spi_flash_spansion_ext2_vi;
+extern const struct spi_flash_vendor_info spi_flash_spansion_vi;
+extern const struct spi_flash_vendor_info spi_flash_sst_ai_vi;
+extern const struct spi_flash_vendor_info spi_flash_sst_vi;
+extern const struct spi_flash_vendor_info spi_flash_stmicro1_vi;
+extern const struct spi_flash_vendor_info spi_flash_stmicro2_vi;
+extern const struct spi_flash_vendor_info spi_flash_stmicro3_vi;
+extern const struct spi_flash_vendor_info spi_flash_stmicro4_vi;
+extern const struct spi_flash_vendor_info spi_flash_winbond_vi;
+
+/* Page Programming Command Set with 0x20 Sector Erase command. */
+extern const struct spi_flash_ops_descriptor spi_flash_pp_0x20_sector_desc;
+/* Page Programming Command Set with 0xd8 Sector Erase command. */
+extern const struct spi_flash_ops_descriptor spi_flash_pp_0xd8_sector_desc;
+
 #endif /* SPI_FLASH_INTERNAL_H */
diff --git a/src/drivers/spi/sst.c b/src/drivers/spi/sst.c
index 1b4ba57..559f30f 100644
--- a/src/drivers/spi/sst.c
+++ b/src/drivers/spi/sst.c
@@ -44,79 +44,51 @@
 #define SST_SR_AAI		(1 << 6)	/* Addressing mode */
 #define SST_SR_BPL		(1 << 7)	/* BP bits lock */
 
-static int sst_write_ai(const struct spi_flash *flash, u32 offset, size_t len,
-			const void *buf);
-
-static const struct spi_flash_ops spi_flash_ops_write_ai = {
-	.read = spi_flash_cmd_read,
-	.write = sst_write_ai,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
-};
-
-static const struct spi_flash_ops spi_flash_ops_write_256 = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
-};
-
 static const struct spi_flash_part_id flash_table_ai[] = {
 	{
 		.id = 0x8d,
 		.name = "SST25VF040B",
 		.nr_sectors_shift = 7,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x8e,
 		.name = "SST25VF080B",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x80,
 		.name = "SST25VF080",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x41,
 		.name = "SST25VF016B",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x4a,
 		.name = "SST25VF032B",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x01,
 		.name = "SST25WF512",
 		.nr_sectors_shift = 4,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x02,
 		.name = "SST25WF010",
 		.nr_sectors_shift = 5,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x03,
 		.name = "SST25WF020",
 		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x04,
 		.name = "SST25WF040",
 		.nr_sectors_shift = 7,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x05,
 		.name = "SST25WF080",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},{
 		.id = 0x14,
 		.name = "SST25WF080B",
 		.nr_sectors_shift = 8,
-		.sector_size_kib_shift = 2,
 	},
 };
 
@@ -125,7 +97,6 @@
 		.id = 0x4b,
 		.name = "SST25VF064C",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 };
 
@@ -247,9 +218,8 @@
 	return ret;
 }
 
-
-static int
-sst_unlock(const struct spi_flash *flash)
+/* Flash powers up read-only, so clear BP# bits */
+static int sst_unlock(const struct spi_flash *flash)
 {
 	int ret;
 	u8 cmd, status;
@@ -269,53 +239,35 @@
 	return ret;
 }
 
-static int match_table(const struct spi_slave *spi, struct spi_flash *flash, u8 id,
-			const struct spi_flash_part_id *parts, size_t num_parts,
-			const struct spi_flash_ops *ops)
-{
-	const struct spi_flash_part_id *params;
-	size_t i;
+static const struct spi_flash_ops_descriptor descai = {
+	.erase_cmd = CMD_SST_SE,
+	.status_cmd = CMD_SST_RDSR,
+	.wren_cmd = CMD_SST_WREN,
+	.ops = {
+		.read = spi_flash_cmd_read,
+		.write = sst_write_ai,
+		.erase = spi_flash_cmd_erase,
+		.status = spi_flash_cmd_status,
+	},
+};
 
-	for (i = 0; i < num_parts; i++) {
-		params = &parts[i];
-		if (params->id == id)
-			break;
-	}
+const struct spi_flash_vendor_info spi_flash_sst_ai_vi = {
+	.id = VENDOR_ID_SST,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xff,
+	.ids = flash_table_ai,
+	.nr_part_ids = ARRAY_SIZE(flash_table_ai),
+	.desc = &descai,
+	.after_probe = sst_unlock,
+};
 
-	if (i == num_parts)
-		return -1;
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_SST_SE;
-	flash->status_cmd = CMD_SST_RDSR;
-	flash->wren_cmd = CMD_SST_WREN;
-
-	flash->ops = ops;
-
-	/* Flash powers up read-only, so clear BP# bits */
-	sst_unlock(flash);
-
-	return 0;
-}
-
-int spi_flash_probe_sst(const struct spi_slave *spi, u8 *idcode,
-			struct spi_flash *flash)
-{
-	if (!match_table(spi, flash, idcode[2], flash_table_ai,
-			ARRAY_SIZE(flash_table_ai), &spi_flash_ops_write_ai))
-		return 0;
-
-	if (!match_table(spi, flash, idcode[2], flash_table_pp256,
-			ARRAY_SIZE(flash_table_pp256), &spi_flash_ops_write_256)) {
-		flash->page_size = 256;
-		flash->pp_cmd = CMD_SST_PP;
-		return 0;
-	}
-
-	printk(BIOS_WARNING, "SF: Unsupported SST ID %02x\n", idcode[2]);
-
-	return -1;
-}
+const struct spi_flash_vendor_info spi_flash_sst_vi = {
+	.id = VENDOR_ID_SST,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xff,
+	.ids = flash_table_pp256,
+	.nr_part_ids = ARRAY_SIZE(flash_table_pp256),
+	.desc = &spi_flash_pp_0x20_sector_desc,
+	.after_probe = sst_unlock,
+};
diff --git a/src/drivers/spi/stmicro.c b/src/drivers/spi/stmicro.c
index 2f4e42a..62755f4 100644
--- a/src/drivers/spi/stmicro.c
+++ b/src/drivers/spi/stmicro.c
@@ -64,102 +64,92 @@
 #define STM_ID_N25Q128__1E	0xbb18
 #define STM_ID_N25Q256__1E	0xbb19
 
-static const struct spi_flash_part_id flash_table_se[] = {
+static const struct spi_flash_part_id flash_table_se32k[] = {
 	{
 		.id = STM_ID_M25P10,
 		.name = "M25P10",
 		.nr_sectors_shift = 2,
-		.sector_size_kib_shift = 5,
 	},
+};
+
+static const struct spi_flash_part_id flash_table_se64k[] = {
 	{
 		.id = STM_ID_M25P16,
 		.name = "M25P16",
 		.nr_sectors_shift = 5,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25P20,
 		.name = "M25P20",
 		.nr_sectors_shift = 2,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25P32,
 		.name = "M25P32",
 		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25P40,
 		.name = "M25P40",
 		.nr_sectors_shift = 3,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25P64,
 		.name = "M25P64",
 		.nr_sectors_shift = 7,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25P80,
 		.name = "M25P80",
 		.nr_sectors_shift = 4,
-		.sector_size_kib_shift = 6,
-	},
-	{
-		.id = STM_ID_M25P128,
-		.name = "M25P128",
-		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 8,
 	},
 	{
 		.id = STM_ID_M25PX80,
 		.name = "M25PX80",
 		.nr_sectors_shift = 4,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25PX16,
 		.name = "M25PX16",
 		.nr_sectors_shift = 5,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25PX32,
 		.name = "M25PX32",
 		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25PX64,
 		.name = "M25PX64",
 		.nr_sectors_shift = 7,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25PE80,
 		.name = "M25PE80",
 		.nr_sectors_shift = 4,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25PE16,
 		.name = "M25PE16",
 		.nr_sectors_shift = 5,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25PE32,
 		.name = "M25PE32",
 		.nr_sectors_shift = 6,
-		.sector_size_kib_shift = 6,
 	},
 	{
 		.id = STM_ID_M25PE64,
 		.name = "M25PE64",
 		.nr_sectors_shift = 7,
-		.sector_size_kib_shift = 6,
+	},
+};
+
+static const struct spi_flash_part_id flash_table_se256k[] = {
+	{
+		.id = STM_ID_M25P128,
+		.name = "M25P128",
+		.nr_sectors_shift = 6,
 	},
 };
 
@@ -168,71 +158,54 @@
 		.id = STM_ID_N25Q016__3E,
 		.name = "N25Q016..3E",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q032__3E,
 		.name = "N25Q032..3E",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q064__3E,
 		.name = "N25Q064..3E",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q128__3E,
 		.name = "N25Q128..3E",
 		.nr_sectors_shift = 12,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q256__3E,
 		.name = "N25Q256..3E",
 		.nr_sectors_shift = 13,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q016__1E,
 		.name = "N25Q016..1E",
 		.nr_sectors_shift = 9,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q032__1E,
 		.name = "N25Q032..1E",
 		.nr_sectors_shift = 10,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q064__1E,
 		.name = "N25Q064..1E",
 		.nr_sectors_shift = 11,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q128__1E,
 		.name = "N25Q128..1E",
 		.nr_sectors_shift = 12,
-		.sector_size_kib_shift = 2,
 	},
 	{
 		.id = STM_ID_N25Q256__1E,
 		.name = "N25Q256..1E",
 		.nr_sectors_shift = 13,
-		.sector_size_kib_shift = 2,
 	},
 };
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
-};
-
 int stmicro_release_deep_sleep_identify(const struct spi_slave *spi, u8 *idcode)
 {
 	if (spi_flash_cmd(spi, CMD_M25PXX_RES, idcode, 4))
@@ -251,53 +224,42 @@
 	return 0;
 }
 
-static int match_table(const struct spi_slave *spi, struct spi_flash *flash, u16 id,
-			const struct spi_flash_part_id *parts, size_t num_parts,
-			u8 erase_cmd)
-{
-	const struct spi_flash_part_id *params;
-	size_t i;
+const struct spi_flash_vendor_info spi_flash_stmicro1_vi = {
+	.id = VENDOR_ID_STMICRO,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 5,
+	.match_id_mask = 0xffff,
+	.ids = flash_table_se32k,
+	.nr_part_ids = ARRAY_SIZE(flash_table_se32k),
+	.desc = &spi_flash_pp_0xd8_sector_desc,
+};
 
-	for (i = 0; i < num_parts; i++) {
-		params = &parts[i];
-		if (params->id == id)
-			break;
-	}
+const struct spi_flash_vendor_info spi_flash_stmicro2_vi = {
+	.id = VENDOR_ID_STMICRO,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 6,
+	.match_id_mask = 0xffff,
+	.ids = flash_table_se64k,
+	.nr_part_ids = ARRAY_SIZE(flash_table_se64k),
+	.desc = &spi_flash_pp_0xd8_sector_desc,
+};
 
-	if (i == num_parts) {
-		printk(BIOS_WARNING, "SF: Unsupported STMicro ID %04x\n", id);
-		return -1;
-	}
+const struct spi_flash_vendor_info spi_flash_stmicro3_vi = {
+	.id = VENDOR_ID_STMICRO,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 8,
+	.match_id_mask = 0xffff,
+	.ids = flash_table_se256k,
+	.nr_part_ids = ARRAY_SIZE(flash_table_se256k),
+	.desc = &spi_flash_pp_0xd8_sector_desc,
+};
 
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = erase_cmd;
-	flash->status_cmd = CMD_M25PXX_RDSR;
-	flash->pp_cmd = CMD_M25PXX_PP;
-	flash->wren_cmd = CMD_M25PXX_WREN;
-
-	flash->ops = &spi_flash_ops;
-
-	return 0;
-}
-
-int spi_flash_probe_stmicro(const struct spi_slave *spi, u8 *idcode,
-			    struct spi_flash *flash)
-{
-	u16 id = (idcode[1] << 8) | idcode[2];
-
-	if (!match_table(spi, flash, id, flash_table_se, ARRAY_SIZE(flash_table_se),
-		CMD_M25PXX_SE)) {
-		return 0;
-	}
-
-	if (!match_table(spi, flash, id, flash_table_sse, ARRAY_SIZE(flash_table_sse),
-		CMD_M25PXX_SSE)) {
-		return 0;
-	}
-
-	return -1;
-}
+const struct spi_flash_vendor_info spi_flash_stmicro4_vi = {
+	.id = VENDOR_ID_STMICRO,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table_sse,
+	.nr_part_ids = ARRAY_SIZE(flash_table_sse),
+	.desc = &spi_flash_pp_0x20_sector_desc,
+};
diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c
index 6766bb6..e85f59d 100644
--- a/src/drivers/spi/winbond.c
+++ b/src/drivers/spi/winbond.c
@@ -84,60 +84,51 @@
 		.id				= 0x2014,
 		.name				= "W25P80",
 		.nr_sectors_shift		= 8,
-		.sector_size_kib_shift		= 2,
 	},
 	{
 		.id				= 0x2015,
 		.name				= "W25P16",
 		.nr_sectors_shift		= 9,
-		.sector_size_kib_shift		= 2,
 	},
 	{
 		.id				= 0x2016,
 		.name				= "W25P32",
 		.nr_sectors_shift		= 10,
-		.sector_size_kib_shift		= 2,
 	},
 	{
 		.id				= 0x3014,
 		.name				= "W25X80",
 		.nr_sectors_shift		= 8,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x3015,
 		.name				= "W25X16",
 		.nr_sectors_shift		= 9,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x3016,
 		.name				= "W25X32",
 		.nr_sectors_shift		= 10,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x3017,
 		.name				= "W25X64",
 		.nr_sectors_shift		= 11,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x4014,
 		.name				= "W25Q80_V",
 		.nr_sectors_shift		= 8,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 	},
 	{
 		.id				= 0x4015,
 		.name				= "W25Q16_V",
 		.nr_sectors_shift		= 9,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 16,
 		.bp_bits			= 3,
@@ -146,7 +137,6 @@
 		.id				= 0x6015,
 		.name				= "W25Q16DW",
 		.nr_sectors_shift		= 9,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 16,
 		.bp_bits			= 3,
@@ -155,7 +145,6 @@
 		.id				= 0x4016,
 		.name				= "W25Q32_V",
 		.nr_sectors_shift		= 10,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 16,
 		.bp_bits			= 3,
@@ -164,7 +153,6 @@
 		.id				= 0x6016,
 		.name				= "W25Q32DW",
 		.nr_sectors_shift		= 10,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 16,
 		.bp_bits			= 3,
@@ -173,7 +161,6 @@
 		.id				= 0x4017,
 		.name				= "W25Q64_V",
 		.nr_sectors_shift		= 11,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 17,
 		.bp_bits			= 3,
@@ -182,7 +169,6 @@
 		.id				= 0x6017,
 		.name				= "W25Q64DW",
 		.nr_sectors_shift		= 11,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 17,
 		.bp_bits			= 3,
@@ -191,7 +177,6 @@
 		.id				= 0x4018,
 		.name				= "W25Q128_V",
 		.nr_sectors_shift		= 12,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 18,
 		.bp_bits			= 3,
@@ -200,7 +185,6 @@
 		.id				= 0x6018,
 		.name				= "W25Q128FW",
 		.nr_sectors_shift		= 12,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 18,
 		.bp_bits			= 3,
@@ -209,7 +193,6 @@
 		.id				= 0x7018,
 		.name				= "W25Q128J",
 		.nr_sectors_shift		= 12,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 18,
 		.bp_bits			= 3,
@@ -218,7 +201,6 @@
 		.id				= 0x8018,
 		.name				= "W25Q128JW",
 		.nr_sectors_shift		= 12,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 18,
 		.bp_bits			= 3,
@@ -227,7 +209,6 @@
 		.id				= 0x4019,
 		.name				= "W25Q256_V",
 		.nr_sectors_shift		= 13,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 16,
 		.bp_bits			= 4,
@@ -236,7 +217,6 @@
 		.id				= 0x7019,
 		.name				= "W25Q256J",
 		.nr_sectors_shift		= 13,
-		.sector_size_kib_shift		= 2,
 		.fast_read_dual_output_support	= 1,
 		.protection_granularity_shift	= 16,
 		.bp_bits			= 4,
@@ -266,17 +246,6 @@
 	out->size = protected_size;
 }
 
-static const struct spi_flash_part_id *lookup_part(u16 id)
-{
-	size_t i;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
-		if (flash_table[i].id == id)
-			return &flash_table[i];
-	}
-
-	return NULL;
-}
 /*
  * Available on all devices.
  * Read block protect bits from Status/Status2 Reg.
@@ -296,7 +265,7 @@
 	u8 bp, tb;
 	int ret;
 
-	params = lookup_part(flash->model);
+	params = flash->part;
 
 	if (!params)
 		return -1;
@@ -479,7 +448,7 @@
 	if (region_offset(region) != 0 && region_end(region) != flash->size)
 		return -1;
 
-	params = lookup_part(flash->model);
+	params = flash->part;
 
 	if (!params)
 		return -1;
@@ -573,52 +542,18 @@
 	return ret;
 }
 
-static const struct spi_flash_ops spi_flash_ops = {
-	.read = spi_flash_cmd_read,
-	.write = spi_flash_cmd_write_page_program,
-	.erase = spi_flash_cmd_erase,
-	.status = spi_flash_cmd_status,
-};
-
 static const struct spi_flash_protection_ops spi_flash_protection_ops = {
 	.get_write = winbond_get_write_protection,
 	.set_write = winbond_set_write_protection,
 };
 
-int spi_flash_probe_winbond(const struct spi_slave *spi, u8 *idcode,
-			    struct spi_flash *flash)
-{
-	const struct spi_flash_part_id *params;
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(flash_table); i++) {
-		params = &flash_table[i];
-		if (params->id == ((idcode[1] << 8) | idcode[2]))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(flash_table)) {
-		printk(BIOS_WARNING, "SF: Unsupported Winbond ID %02x%02x\n",
-				idcode[1], idcode[2]);
-		return -1;
-	}
-
-	memcpy(&flash->spi, spi, sizeof(*spi));
-	flash->name = params->name;
-
-	/* Params are in power-of-two. */
-	flash->page_size = 256;
-	flash->sector_size = (1U << params->sector_size_kib_shift) * KiB;
-	flash->size = flash->sector_size * (1U << params->nr_sectors_shift);
-	flash->erase_cmd = CMD_W25_SE;
-	flash->status_cmd = CMD_W25_RDSR;
-	flash->pp_cmd = CMD_W25_PP;
-	flash->wren_cmd = CMD_W25_WREN;
-
-	flash->flags.dual_spi = params->fast_read_dual_output_support;
-
-	flash->ops = &spi_flash_ops;
-	flash->prot_ops = &spi_flash_protection_ops;
-
-	return 0;
-}
+const struct spi_flash_vendor_info spi_flash_winbond_vi = {
+	.id = VENDOR_ID_WINBOND,
+	.page_size_shift = 8,
+	.sector_size_kib_shift = 2,
+	.match_id_mask = 0xffff,
+	.ids = flash_table,
+	.nr_part_ids = ARRAY_SIZE(flash_table),
+	.desc = &spi_flash_pp_0x20_sector_desc,
+	.prot_ops = &spi_flash_protection_ops,
+};
diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h
index ffa66db..9acff3b 100644
--- a/src/include/spi_flash.h
+++ b/src/include/spi_flash.h
@@ -91,6 +91,8 @@
 
 };
 
+struct spi_flash_part_id;
+
 struct spi_flash {
 	struct spi_slave spi;
 	u8 vendor;
@@ -113,6 +115,7 @@
 	const struct spi_flash_ops *ops;
 	/* If !NULL all protection callbacks exist. */
 	const struct spi_flash_protection_ops *prot_ops;
+	const struct spi_flash_part_id *part;
 };
 
 void lb_spi_flash(struct lb_header *header);