[coreboot-gerrit] Patch set updated for coreboot: spi: Clean up SPI flash driver interface

Furquan Shaikh (furquan@google.com) gerrit at coreboot.org
Tue Nov 22 06:08:19 CET 2016


Furquan Shaikh (furquan at google.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17462

-gerrit

commit 04ff4d31111b63c29c9689bfcc364583f7896075
Author: Furquan Shaikh <furquan at chromium.org>
Date:   Sun Nov 20 21:04:00 2016 -0800

    spi: Clean up SPI flash driver interface
    
    RW flag was added to spi_slave structure to get around a requirement on
    some AMD flash controllers that need to group together all spi volatile
    operations (write/erase). This rw flag is not a property or attribute of
    the SPI slave or controller. Thus, instead of saving it in spi_slave
    structure, clean up the SPI flash driver interface. This allows
    chipsets/mainboards (that require volatile operations to be grouped) to
    indicate beginning and end of such grouped operations.
    
    New user APIs are added to allow users to perform probe, read, write,
    erase, volatile group begin and end operations. Callbacks defined in
    spi_flash structure are expected to be used only by the SPI flash
    driver. Any chipset that requires grouping of volatile operations can
    select the newly added Kconfig option SPI_FLASH_HAS_VOLATILE_GROUP and
    define callbacks for chipset_volatile_group_{begin,end}.
    
    spi_claim_bus/spi_release_bus calls have been removed from the SPI flash
    chip drivers which end up calling do_spi_flash_cmd since it already has
    required calls for claiming and releasing SPI bus before performing a
    read/write operation.
    
    BUG=None
    BRANCH=None
    TEST=Compiles successfully.
    
    Change-Id: Idfc052e82ec15b6c9fa874cee7a61bd06e923fbf
    Signed-off-by: Furquan Shaikh <furquan at chromium.org>
---
 src/cpu/amd/pi/spi.c                               |  12 +--
 src/drivers/intel/fsp1_0/fastboot_cache.c          |   8 +-
 src/drivers/spi/Kconfig                            |   7 ++
 src/drivers/spi/adesto.c                           |  24 ++---
 src/drivers/spi/amic.c                             |  24 ++---
 src/drivers/spi/atmel.c                            |  24 ++---
 src/drivers/spi/boot_device_rw_nommap.c            |   7 +-
 src/drivers/spi/cbfs_spi.c                         |   7 +-
 src/drivers/spi/eon.c                              |  17 ++--
 src/drivers/spi/gigadevice.c                       |  21 ++---
 src/drivers/spi/macronix.c                         |  22 ++---
 src/drivers/spi/spansion.c                         |  20 ++--
 src/drivers/spi/spi_flash.c                        | 105 ++++++++++++++++++---
 src/drivers/spi/spi_flash_internal.h               |  12 +--
 src/drivers/spi/spiconsole.c                       |   2 +-
 src/drivers/spi/sst.c                              |  42 ++++-----
 src/drivers/spi/stmicro.c                          |  17 ++--
 src/drivers/spi/winbond.c                          |  20 ++--
 src/include/spi-generic.h                          |   5 -
 src/include/spi_flash.h                            |  66 +++++++++----
 src/northbridge/amd/agesa/oem_s3.c                 |  12 +--
 src/northbridge/amd/amdmct/mct_ddr3/s3utils.c      |  13 +--
 src/northbridge/intel/common/mrc_cache.c           |   7 +-
 src/soc/imgtec/pistachio/spi.c                     |   2 +-
 src/soc/intel/apollolake/spi.c                     |  22 +++--
 src/soc/intel/baytrail/spi.c                       |   2 +-
 src/soc/intel/braswell/spi.c                       |   2 +-
 src/soc/intel/common/nvm.c                         |   7 +-
 src/soc/intel/fsp_baytrail/nvm.c                   |   4 +-
 src/soc/intel/fsp_baytrail/spi.c                   |   2 +-
 src/soc/intel/skylake/flash_controller.c           |  23 +++--
 .../intel/skylake/include/soc/flash_controller.h   |  13 ++-
 src/soc/intel/skylake/romstage/spi.c               |   1 +
 src/soc/mediatek/mt8173/flash_controller.c         |  17 ++--
 src/soc/qualcomm/ipq40xx/include/soc/spi.h         |   2 +-
 src/soc/qualcomm/ipq40xx/spi.c                     |   1 +
 src/soc/qualcomm/ipq806x/include/soc/spi.h         |   2 +-
 src/soc/qualcomm/ipq806x/spi.c                     |   1 +
 src/soc/rockchip/common/spi.c                      |  27 ++----
 src/soc/samsung/exynos5420/spi.c                   |   7 +-
 src/southbridge/amd/agesa/hudson/Kconfig           |   1 +
 src/southbridge/amd/agesa/hudson/spi.c             |  43 +++++----
 src/southbridge/amd/cimx/sb800/Kconfig             |   1 +
 src/southbridge/amd/cimx/sb800/spi.c               |  46 +++++----
 src/southbridge/intel/common/spi.c                 |  18 ++--
 45 files changed, 406 insertions(+), 332 deletions(-)

diff --git a/src/cpu/amd/pi/spi.c b/src/cpu/amd/pi/spi.c
index f571805..71b5d95 100644
--- a/src/cpu/amd/pi/spi.c
+++ b/src/cpu/amd/pi/spi.c
@@ -31,15 +31,13 @@ void spi_SaveS3info(u32 pos, u32 size, u8 *buf, u32 len)
 		return;
 	}
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-	spi_claim_bus(flash->spi);
+	spi_flash_volatile_group_begin(flash);
 
-	flash->erase(flash, pos, size);
-	flash->write(flash, pos, sizeof(len), &len);
-	flash->write(flash, pos + sizeof(len), len, buf);
+	spi_flash_erase(flash, pos, size);
+	spi_flash_write(flash, pos, sizeof(len), &len);
+	spi_flash_write(flash, pos + sizeof(len), len, buf);
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-	spi_release_bus(flash->spi);
+	spi_flash_volatile_group_end(flash);
 
 	return;
 }
diff --git a/src/drivers/intel/fsp1_0/fastboot_cache.c b/src/drivers/intel/fsp1_0/fastboot_cache.c
index 68150f9..b57f382 100644
--- a/src/drivers/intel/fsp1_0/fastboot_cache.c
+++ b/src/drivers/intel/fsp1_0/fastboot_cache.c
@@ -209,16 +209,16 @@ void update_mrc_cache(void *unused)
 		       "Need to erase the MRC cache region of %d bytes at %p\n",
 		       cache_size, cache_base);
 
-		flash->erase(flash, to_flash_offset(cache_base), cache_size);
+		spi_flash_erase(flash, to_flash_offset(cache_base), cache_size);
 
 		/* we will start at the beginning again */
 		cache = cache_base;
 	}
-	/*  4. write mrc data with flash->write() */
+	/*  4. write mrc data with spi_flash_write() */
 	printk(BIOS_DEBUG, "Write MRC cache update to flash at %p\n",
 	       cache);
-	flash->write(flash, to_flash_offset(cache),
-		     current->mrc_data_size + sizeof(*current), current);
+	spi_flash_write(flash, to_flash_offset(cache),
+			current->mrc_data_size + sizeof(*current), current);
 }
 
 #endif	/* !defined(__PRE_RAM__) */
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig
index 4c2fd1f..b55de58 100644
--- a/src/drivers/spi/Kconfig
+++ b/src/drivers/spi/Kconfig
@@ -164,6 +164,13 @@ config SPI_FLASH_FAST_READ_DUAL_OUTPUT_3B
 	  output command (opcode 0x3b) where the opcode and address are sent
 	  to the chip on MOSI and data is received on both MOSI and MISO.
 
+config SPI_FLASH_HAS_VOLATILE_GROUP
+	bool
+	default n
+	help
+	  Allows chipset to group write/erase operations under a single volatile
+	  group.
+
 endif # SPI_FLASH
 
 config HAVE_SPI_CONSOLE_SUPPORT
diff --git a/src/drivers/spi/adesto.c b/src/drivers/spi/adesto.c
index fdde90f..91bb774 100644
--- a/src/drivers/spi/adesto.c
+++ b/src/drivers/spi/adesto.c
@@ -10,8 +10,10 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -46,7 +48,7 @@ struct adesto_spi_flash {
 };
 
 static inline struct adesto_spi_flash *
-to_adesto_spi_flash(struct spi_flash *flash)
+to_adesto_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct adesto_spi_flash, flash);
 }
@@ -78,8 +80,8 @@ static const struct adesto_spi_flash_params adesto_spi_flash_table[] = {
 	},
 };
 
-static int adesto_write(struct spi_flash *flash,
-		u32 offset, size_t len, const void *buf)
+static int adesto_write(const struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf)
 {
 	struct adesto_spi_flash *stm = to_adesto_spi_flash(flash);
 	unsigned long byte_addr;
@@ -92,13 +94,6 @@ static int adesto_write(struct spi_flash *flash,
 	page_size = 1 << stm->params->l2_page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-	ret = spi_claim_bus(flash->spi);
-	if (ret) {
-		printk(BIOS_WARNING, "SF: Unable to claim SPI bus\n");
-		return ret;
-	}
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -141,7 +136,6 @@ static int adesto_write(struct spi_flash *flash,
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -177,12 +171,12 @@ struct spi_flash *spi_flash_probe_adesto(struct spi_slave *spi, u8 *idcode)
 	/* Assuming power-of-two page size initially. */
 	page_size = 1 << params->l2_page_size;
 
-	stm->flash.write = adesto_write;
-	stm->flash.erase = spi_flash_cmd_erase;
+	stm->flash.internal_write = adesto_write;
+	stm->flash.internal_erase = spi_flash_cmd_erase;
 #if CONFIG_SPI_FLASH_NO_FAST_READ
-	stm->flash.read = spi_flash_cmd_read_slow;
+	stm->flash.internal_read = spi_flash_cmd_read_slow;
 #else
-	stm->flash.read = spi_flash_cmd_read_fast;
+	stm->flash.internal_read = spi_flash_cmd_read_fast;
 #endif
 	stm->flash.sector_size = (1 << stm->params->l2_page_size) *
 		stm->params->pages_per_sector;
diff --git a/src/drivers/spi/amic.c b/src/drivers/spi/amic.c
index e5f6672..4f3002c 100644
--- a/src/drivers/spi/amic.c
+++ b/src/drivers/spi/amic.c
@@ -8,8 +8,10 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -44,7 +46,7 @@ struct amic_spi_flash {
 };
 
 static inline struct amic_spi_flash *
-to_amic_spi_flash(struct spi_flash *flash)
+to_amic_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct amic_spi_flash, flash);
 }
@@ -60,8 +62,8 @@ static const struct amic_spi_flash_params amic_spi_flash_table[] = {
 	},
 };
 
-static int amic_write(struct spi_flash *flash,
-		u32 offset, size_t len, const void *buf)
+static int amic_write(const struct spi_flash *flash, u32 offset, size_t len,
+		const void *buf)
 {
 	struct amic_spi_flash *amic = to_amic_spi_flash(flash);
 	unsigned long byte_addr;
@@ -74,13 +76,6 @@ static int amic_write(struct spi_flash *flash,
 	page_size = 1 << amic->params->l2_page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-	ret = spi_claim_bus(flash->spi);
-	if (ret) {
-		printk(BIOS_WARNING, "SF: Unable to claim SPI bus\n");
-		return ret;
-	}
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -123,7 +118,6 @@ static int amic_write(struct spi_flash *flash,
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -159,12 +153,12 @@ struct spi_flash *spi_flash_probe_amic(struct spi_slave *spi, u8 *idcode)
 	/* Assuming power-of-two page size initially. */
 	page_size = 1 << params->l2_page_size;
 
-	amic->flash.write = amic_write;
-	amic->flash.erase = spi_flash_cmd_erase;
+	amic->flash.internal_write = amic_write;
+	amic->flash.internal_erase = spi_flash_cmd_erase;
 #if CONFIG_SPI_FLASH_NO_FAST_READ
-	amic->flash.read = spi_flash_cmd_read_slow;
+	amic->flash.internal_read = spi_flash_cmd_read_slow;
 #else
-	amic->flash.read = spi_flash_cmd_read_fast;
+	amic->flash.internal_read = spi_flash_cmd_read_fast;
 #endif
 	amic->flash.sector_size = (1 << amic->params->l2_page_size) *
 		amic->params->pages_per_sector;
diff --git a/src/drivers/spi/atmel.c b/src/drivers/spi/atmel.c
index 0286e09..e538892 100644
--- a/src/drivers/spi/atmel.c
+++ b/src/drivers/spi/atmel.c
@@ -6,8 +6,10 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 #include "spi_flash_internal.h"
 
 /* M25Pxx-specific commands */
@@ -41,7 +43,7 @@ struct atmel_spi_flash {
 };
 
 static inline struct atmel_spi_flash *
-to_atmel_spi_flash(struct spi_flash *flash)
+to_atmel_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct atmel_spi_flash, flash);
 }
@@ -105,8 +107,8 @@ static const struct atmel_spi_flash_params atmel_spi_flash_table[] = {
 	},
 };
 
-static int atmel_write(struct spi_flash *flash,
-		u32 offset, size_t len, const void *buf)
+static int atmel_write(const struct spi_flash *flash, u32 offset, size_t len,
+		const void *buf)
 {
 	struct atmel_spi_flash *stm = to_atmel_spi_flash(flash);
 	unsigned long byte_addr;
@@ -119,13 +121,6 @@ static int atmel_write(struct spi_flash *flash,
 	page_size = 1 << stm->params->l2_page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-	ret = spi_claim_bus(flash->spi);
-	if (ret) {
-		printk(BIOS_WARNING, "SF: Unable to claim SPI bus\n");
-		return ret;
-	}
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -168,7 +163,6 @@ static int atmel_write(struct spi_flash *flash,
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -204,12 +198,12 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
 	/* Assuming power-of-two page size initially. */
 	page_size = 1 << params->l2_page_size;
 
-	stm->flash.write = atmel_write;
-	stm->flash.erase = spi_flash_cmd_erase;
+	stm->flash.internal_write = atmel_write;
+	stm->flash.internal_erase = spi_flash_cmd_erase;
 #if CONFIG_SPI_FLASH_NO_FAST_READ
-	stm->flash.read = spi_flash_cmd_read_slow;
+	stm->flash.internal_read = spi_flash_cmd_read_slow;
 #else
-	stm->flash.read = spi_flash_cmd_read_fast;
+	stm->flash.internal_read = spi_flash_cmd_read_fast;
 #endif
 	stm->flash.sector_size = (1 << stm->params->l2_page_size) *
 		stm->params->pages_per_sector;
diff --git a/src/drivers/spi/boot_device_rw_nommap.c b/src/drivers/spi/boot_device_rw_nommap.c
index 32ded9c..a52a7be 100644
--- a/src/drivers/spi/boot_device_rw_nommap.c
+++ b/src/drivers/spi/boot_device_rw_nommap.c
@@ -16,6 +16,7 @@
 #include <arch/early_variables.h>
 #include <boot_device.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 static struct spi_flash *sfg CAR_GLOBAL;
 
@@ -27,7 +28,7 @@ static ssize_t spi_readat(const struct region_device *rd, void *b,
 	if (sf == NULL)
 		return -1;
 
-	if (sf->read(sf, offset, size, b))
+	if (spi_flash_read(sf, offset, size, b))
 		return -1;
 
 	return size;
@@ -41,7 +42,7 @@ static ssize_t spi_writeat(const struct region_device *rd, const void *b,
 	if (sf == NULL)
 		return -1;
 
-	if (sf->write(sf, offset, size, b))
+	if (spi_flash_write(sf, offset, size, b))
 		return -1;
 
 	return size;
@@ -55,7 +56,7 @@ static ssize_t spi_eraseat(const struct region_device *rd,
 	if (sf == NULL)
 		return -1;
 
-	if (sf->erase(sf, offset, size))
+	if (spi_flash_erase(sf, offset, size))
 		return -1;
 
 	return size;
diff --git a/src/drivers/spi/cbfs_spi.c b/src/drivers/spi/cbfs_spi.c
index 46e7346..7aeec57 100644
--- a/src/drivers/spi/cbfs_spi.c
+++ b/src/drivers/spi/cbfs_spi.c
@@ -20,6 +20,7 @@
  */
 
 #include <boot_device.h>
+#include <console/console.h>
 #include <spi_flash.h>
 #include <symbols.h>
 #include <cbmem.h>
@@ -46,7 +47,7 @@ static ssize_t spi_readat(const struct region_device *rd, void *b,
 
 	if (show)
 		stopwatch_init(&sw);
-	if (spi_flash_info->read(spi_flash_info, offset, size, b))
+	if (spi_flash_read(spi_flash_info, offset, size, b))
 		return -1;
 	if (show) {
 		long usecs;
@@ -67,7 +68,7 @@ static ssize_t spi_readat(const struct region_device *rd, void *b,
 static ssize_t spi_writeat(const struct region_device *rd, const void *b,
 				size_t offset, size_t size)
 {
-	if (spi_flash_info->write(spi_flash_info, offset, size, b))
+	if (spi_flash_write(spi_flash_info, offset, size, b))
 		return -1;
 	return size;
 }
@@ -75,7 +76,7 @@ static ssize_t spi_writeat(const struct region_device *rd, const void *b,
 static ssize_t spi_eraseat(const struct region_device *rd,
 				size_t offset, size_t size)
 {
-	if (spi_flash_info->erase(spi_flash_info, offset, size))
+	if (spi_flash_erase(spi_flash_info, offset, size))
 		return -1;
 	return size;
 }
diff --git a/src/drivers/spi/eon.c b/src/drivers/spi/eon.c
index 923b0a5..6430766 100644
--- a/src/drivers/spi/eon.c
+++ b/src/drivers/spi/eon.c
@@ -6,8 +6,10 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -43,7 +45,8 @@ struct eon_spi_flash {
 	const struct eon_spi_flash_params *params;
 };
 
-static inline struct eon_spi_flash *to_eon_spi_flash(struct spi_flash *flash)
+static inline
+struct eon_spi_flash *to_eon_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct eon_spi_flash, flash);
 }
@@ -75,7 +78,7 @@ static const struct eon_spi_flash_params eon_spi_flash_table[] = {
 	},
 };
 
-static int eon_write(struct spi_flash *flash,
+static int eon_write(const struct spi_flash *flash,
 		     u32 offset, size_t len, const void *buf)
 {
 	struct eon_spi_flash *eon = to_eon_spi_flash(flash);
@@ -89,8 +92,6 @@ static int eon_write(struct spi_flash *flash,
 	page_size = 1 << eon->params->page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -166,10 +167,10 @@ struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
 	eon->flash.spi = spi;
 	eon->flash.name = params->name;
 
-	eon->flash.write = eon_write;
-	eon->flash.erase = spi_flash_cmd_erase;
-	eon->flash.status = spi_flash_cmd_status;
-	eon->flash.read = spi_flash_cmd_read_fast;
+	eon->flash.internal_write = eon_write;
+	eon->flash.internal_erase = spi_flash_cmd_erase;
+	eon->flash.internal_status = spi_flash_cmd_status;
+	eon->flash.internal_read = spi_flash_cmd_read_fast;
 	eon->flash.sector_size = params->page_size * params->pages_per_sector;
 	eon->flash.size = params->page_size * params->pages_per_sector
 	    * params->nr_sectors;
diff --git a/src/drivers/spi/gigadevice.c b/src/drivers/spi/gigadevice.c
index 780cc82..3146a97 100644
--- a/src/drivers/spi/gigadevice.c
+++ b/src/drivers/spi/gigadevice.c
@@ -17,9 +17,10 @@
  * GNU General Public License for more details.
  */
 
-
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -54,7 +55,7 @@ struct gigadevice_spi_flash {
 };
 
 static inline struct gigadevice_spi_flash *
-to_gigadevice_spi_flash(struct spi_flash *flash)
+to_gigadevice_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct gigadevice_spi_flash, flash);
 }
@@ -118,8 +119,8 @@ static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
 	},
 };
 
-static int gigadevice_write(struct spi_flash *flash, u32 offset,
-			    size_t len, const void *buf)
+static int gigadevice_write(const struct spi_flash *flash, u32 offset,
+			size_t len, const void *buf)
 {
 	struct gigadevice_spi_flash *stm = to_gigadevice_spi_flash(flash);
 	unsigned long byte_addr;
@@ -132,8 +133,6 @@ static int gigadevice_write(struct spi_flash *flash, u32 offset,
 	page_size = 1 << stm->params->l2_page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -212,13 +211,13 @@ struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode)
 	/* Assuming power-of-two page size initially. */
 	page_size = 1 << params->l2_page_size;
 
-	stm.flash.write = gigadevice_write;
-	stm.flash.erase = spi_flash_cmd_erase;
-	stm.flash.status = spi_flash_cmd_status;
+	stm.flash.internal_write = gigadevice_write;
+	stm.flash.internal_erase = spi_flash_cmd_erase;
+	stm.flash.internal_status = spi_flash_cmd_status;
 #if CONFIG_SPI_FLASH_NO_FAST_READ
-	stm.flash.read = spi_flash_cmd_read_slow;
+	stm.flash.internal_read = spi_flash_cmd_read_slow;
 #else
-	stm.flash.read = spi_flash_cmd_read_fast;
+	stm.flash.internal_read = spi_flash_cmd_read_fast;
 #endif
 	stm.flash.sector_size = (1 << stm.params->l2_page_size) *
 		stm.params->pages_per_sector;
diff --git a/src/drivers/spi/macronix.c b/src/drivers/spi/macronix.c
index 09cb80c..799cc97 100644
--- a/src/drivers/spi/macronix.c
+++ b/src/drivers/spi/macronix.c
@@ -21,8 +21,10 @@
  * GNU General Public License for more details.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -56,8 +58,8 @@ struct macronix_spi_flash {
 	const struct macronix_spi_flash_params *params;
 };
 
-static inline struct macronix_spi_flash *to_macronix_spi_flash(struct spi_flash
-							       *flash)
+static inline
+struct macronix_spi_flash *to_macronix_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct macronix_spi_flash, flash);
 }
@@ -145,8 +147,8 @@ static const struct macronix_spi_flash_params macronix_spi_flash_table[] = {
 	},
 };
 
-static int macronix_write(struct spi_flash *flash,
-			  u32 offset, size_t len, const void *buf)
+static int macronix_write(const struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf)
 {
 	struct macronix_spi_flash *mcx = to_macronix_spi_flash(flash);
 	unsigned long byte_addr;
@@ -159,8 +161,6 @@ static int macronix_write(struct spi_flash *flash,
 	page_size = mcx->params->page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -227,13 +227,13 @@ struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode)
 	mcx.flash.spi = spi;
 	mcx.flash.name = params->name;
 
-	mcx.flash.write = macronix_write;
-	mcx.flash.erase = spi_flash_cmd_erase;
-	mcx.flash.status = spi_flash_cmd_status;
+	mcx.flash.internal_write = macronix_write;
+	mcx.flash.internal_erase = spi_flash_cmd_erase;
+	mcx.flash.internal_status = spi_flash_cmd_status;
 #if CONFIG_SPI_FLASH_NO_FAST_READ
-	mcx.flash.read = spi_flash_cmd_read_slow;
+	mcx.flash.internal_read = spi_flash_cmd_read_slow;
 #else
-	mcx.flash.read = spi_flash_cmd_read_fast;
+	mcx.flash.internal_read = spi_flash_cmd_read_fast;
 #endif
 	mcx.flash.sector_size = params->page_size * params->pages_per_sector;
 	mcx.flash.size = mcx.flash.sector_size * params->sectors_per_block *
diff --git a/src/drivers/spi/spansion.c b/src/drivers/spi/spansion.c
index fc6cef9..95b1baf 100644
--- a/src/drivers/spi/spansion.c
+++ b/src/drivers/spi/spansion.c
@@ -17,8 +17,10 @@
  * GNU General Public License for more details.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -66,8 +68,8 @@ struct spansion_spi_flash {
 	const struct spansion_spi_flash_params *params;
 };
 
-static inline struct spansion_spi_flash *to_spansion_spi_flash(struct spi_flash
-							     *flash)
+static inline
+struct spansion_spi_flash *to_spansion_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct spansion_spi_flash, flash);
 }
@@ -198,8 +200,8 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
 	},
 };
 
-static int spansion_write(struct spi_flash *flash,
-			 u32 offset, size_t len, const void *buf)
+static int spansion_write(const struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf)
 {
 	struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
 	unsigned long page_addr;
@@ -214,8 +216,6 @@ static int spansion_write(struct spi_flash *flash,
 	page_addr = offset / page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 
@@ -286,10 +286,10 @@ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
 	spsn->flash.spi = spi;
 	spsn->flash.name = params->name;
 
-	spsn->flash.write = spansion_write;
-	spsn->flash.erase = spi_flash_cmd_erase;
-	spsn->flash.read = spi_flash_cmd_read_slow;
-	spsn->flash.status = spi_flash_cmd_status;
+	spsn->flash.internal_write = spansion_write;
+	spsn->flash.internal_erase = spi_flash_cmd_erase;
+	spsn->flash.internal_read = spi_flash_cmd_read_slow;
+	spsn->flash.internal_status = spi_flash_cmd_status;
 	spsn->flash.sector_size = params->page_size * params->pages_per_sector;
 	spsn->flash.size = spsn->flash.sector_size * params->nr_sectors;
 	spsn->flash.erase_cmd = CMD_S25FLXX_SE;
diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c
index 6006420..7ca6822 100644
--- a/src/drivers/spi/spi_flash.c
+++ b/src/drivers/spi/spi_flash.c
@@ -7,6 +7,8 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <arch/early_variables.h>
+#include <assert.h>
 #include <boot_device.h>
 #include <cbfs.h>
 #include <cpu/x86/smm.h>
@@ -142,8 +144,8 @@ static int spi_flash_cmd_read_array(struct spi_slave *spi, u8 *cmd,
 	return len != 0;
 }
 
-int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
-		size_t len, void *data)
+int spi_flash_cmd_read_fast(const struct spi_flash *flash, u32 offset,
+			size_t len, void *data)
 {
 	u8 cmd[5];
 
@@ -154,8 +156,8 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
 					offset, len, data);
 }
 
-int spi_flash_cmd_read_slow(struct spi_flash *flash, u32 offset,
-			    size_t len, void *data)
+int spi_flash_cmd_read_slow(const struct spi_flash *flash, u32 offset,
+			size_t len, void *data)
 {
 	u8 cmd[4];
 
@@ -164,7 +166,7 @@ int spi_flash_cmd_read_slow(struct spi_flash *flash, u32 offset,
 					offset, len, data);
 }
 
-int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout,
+int spi_flash_cmd_poll_bit(const struct spi_flash *flash, unsigned long timeout,
 			   u8 cmd, u8 poll_bit)
 {
 	struct spi_slave *spi = flash->spi;
@@ -189,13 +191,14 @@ int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout,
 	return -1;
 }
 
-int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
+int spi_flash_cmd_wait_ready(const struct spi_flash *flash,
+			unsigned long timeout)
 {
 	return spi_flash_cmd_poll_bit(flash, timeout,
 		CMD_READ_STATUS, STATUS_WIP);
 }
 
-int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
+int spi_flash_cmd_erase(const struct spi_flash *flash, u32 offset, size_t len)
 {
 	u32 start, end, erase_size;
 	int ret;
@@ -207,8 +210,6 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
 		return -1;
 	}
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	cmd[0] = flash->erase_cmd;
 	start = offset;
 	end = start + len;
@@ -240,7 +241,7 @@ out:
 	return ret;
 }
 
-int spi_flash_cmd_status(struct spi_flash *flash, u8 *reg)
+int spi_flash_cmd_status(const struct spi_flash *flash, u8 *reg)
 {
 	return spi_flash_cmd(flash->spi, flash->status_cmd, reg, sizeof(*reg));
 }
@@ -312,6 +313,8 @@ static struct {
 };
 #define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN)
 
+
+/* Public API implementations. */
 struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs)
 {
 	struct spi_slave *spi;
@@ -325,8 +328,6 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs)
 		return NULL;
 	}
 
-	spi->rw = SPI_READ_FLAG;
-
 	if (spi->force_programmer_specific && spi->programmer_specific_probe) {
 		flash = spi->programmer_specific_probe (spi);
 		if (!flash)
@@ -388,6 +389,86 @@ err_read_id:
 	return NULL;
 }
 
+int spi_flash_read(const struct spi_flash *flash, u32 offset, size_t len,
+		void *buf)
+{
+	return flash->internal_read(flash, offset, len, buf);
+}
+
+int spi_flash_write(const struct spi_flash *flash, u32 offset, size_t len,
+		const void *buf)
+{
+	int ret;
+
+	if (spi_flash_volatile_group_begin(flash))
+		return -1;
+
+	ret = flash->internal_write(flash, offset, len, buf);
+
+	if (spi_flash_volatile_group_end(flash))
+		return -1;
+
+	return ret;
+}
+
+int spi_flash_erase(const struct spi_flash *flash, u32 offset, size_t len)
+{
+	int ret;
+
+	if (spi_flash_volatile_group_begin(flash))
+		return -1;
+
+	ret = flash->internal_erase(flash, offset, len);
+
+	if (spi_flash_volatile_group_end(flash))
+		return -1;
+
+	return ret;
+}
+
+int spi_flash_status(const struct spi_flash *flash, u8 *reg)
+{
+	return flash->internal_status(flash, reg);
+}
+
+static uint32_t volatile_group_count CAR_GLOBAL;
+
+int spi_flash_volatile_group_begin(const struct spi_flash *flash)
+{
+	uint32_t count;
+	int ret = 0;
+
+	if (!IS_ENABLED(CONFIG_SPI_FLASH_HAS_VOLATILE_GROUP))
+		return ret;
+
+	count = car_get_var(volatile_group_count);
+	if (count == 0)
+		ret = chipset_volatile_group_begin(flash);
+
+	count++;
+	car_set_var(volatile_group_count, count);
+	return ret;
+}
+
+int spi_flash_volatile_group_end(const struct spi_flash *flash)
+{
+	uint32_t count;
+	int ret = 0;
+
+	if (!IS_ENABLED(CONFIG_SPI_FLASH_HAS_VOLATILE_GROUP))
+		return ret;
+
+	count = car_get_var(volatile_group_count);
+	assert(count == 0);
+	count--;
+	car_set_var(volatile_group_count, count);
+
+	if (count == 0)
+		ret = chipset_volatile_group_end(flash);
+
+	return ret;
+}
+
 void lb_spi_flash(struct lb_header *header)
 {
 	struct lb_spi_flash *flash;
diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h
index fec3dcc..c758393 100644
--- a/src/drivers/spi/spi_flash_internal.h
+++ b/src/drivers/spi/spi_flash_internal.h
@@ -34,10 +34,10 @@
 /* Send a single-byte command to the device and read the response */
 int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
 
-int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
+int spi_flash_cmd_read_fast(const struct spi_flash *flash, u32 offset,
 		size_t len, void *data);
 
-int spi_flash_cmd_read_slow(struct spi_flash *flash, u32 offset,
+int spi_flash_cmd_read_slow(const struct spi_flash *flash, u32 offset,
 		size_t len, void *data);
 
 /*
@@ -48,20 +48,20 @@ int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
 		const void *data, size_t data_len);
 
 /* Send a command to the device and wait for some bit to clear itself. */
-int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout,
+int spi_flash_cmd_poll_bit(const struct spi_flash *flash, unsigned long timeout,
 			   u8 cmd, u8 poll_bit);
 
 /*
  * Send the read status command to the device and wait for the wip
  * (write-in-progress) bit to clear itself.
  */
-int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout);
+int spi_flash_cmd_wait_ready(const struct spi_flash *flash, unsigned long timeout);
 
 /* Erase sectors. */
-int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len);
+int spi_flash_cmd_erase(const struct spi_flash *flash, u32 offset, size_t len);
 
 /* Read status register. */
-int spi_flash_cmd_status(struct spi_flash *flash, u8 *reg);
+int spi_flash_cmd_status(const struct spi_flash *flash, u8 *reg);
 
 /* Manufacturer-specific probe functions */
 struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode);
diff --git a/src/drivers/spi/spiconsole.c b/src/drivers/spi/spiconsole.c
index a2a1eff..0831f2c 100644
--- a/src/drivers/spi/spiconsole.c
+++ b/src/drivers/spi/spiconsole.c
@@ -54,7 +54,7 @@ void spiconsole_tx_byte(unsigned char c) {
 	if (c == '\n' || (sizeof(struct em100_msg_header) +
 			msg.header.msg_length == spi_crop_chunk(0,
 			MAX_MSG_LENGTH))) {
-		struct spi_slave spi = {.rw = SPI_READ_FLAG};
+		struct spi_slave spi = { };
 
 		spi_xfer(&spi, &msg, sizeof(struct em100_msg_header) +
 				msg.header.msg_length, NULL, 0);
diff --git a/src/drivers/spi/sst.c b/src/drivers/spi/sst.c
index 87c1c5b..940d894 100644
--- a/src/drivers/spi/sst.c
+++ b/src/drivers/spi/sst.c
@@ -12,8 +12,10 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -40,7 +42,7 @@ struct sst_spi_flash_params {
 	u8 idcode1;
 	u16 nr_sectors;
 	const char *name;
-	int (*write)(struct spi_flash *flash, u32 offset,
+	int (*write)(const struct spi_flash *flash, u32 offset,
 				 size_t len, const void *buf);
 };
 
@@ -49,10 +51,10 @@ struct sst_spi_flash {
 	const struct sst_spi_flash_params *params;
 };
 
-static int
-sst_write_ai(struct spi_flash *flash, u32 offset, size_t len, const void *buf);
-static int
-sst_write_256(struct spi_flash *flash, u32 offset, size_t len, const void *buf);
+static int sst_write_ai(const struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf);
+static int sst_write_256(const struct spi_flash *flash, u32 offset, size_t len,
+			 const void *buf);
 
 #define SST_SECTOR_SIZE (4 * 1024)
 static const struct sst_spi_flash_params sst_spi_flash_table[] = {
@@ -105,7 +107,7 @@ static const struct sst_spi_flash_params sst_spi_flash_table[] = {
 };
 
 static int
-sst_enable_writing(struct spi_flash *flash)
+sst_enable_writing(const struct spi_flash *flash)
 {
 	int ret = spi_flash_cmd(flash->spi, CMD_SST_WREN, NULL, 0);
 	if (ret)
@@ -114,7 +116,7 @@ sst_enable_writing(struct spi_flash *flash)
 }
 
 static int
-sst_enable_writing_status(struct spi_flash *flash)
+sst_enable_writing_status(const struct spi_flash *flash)
 {
 	int ret = spi_flash_cmd(flash->spi, CMD_SST_EWSR, NULL, 0);
 	if (ret)
@@ -123,7 +125,7 @@ sst_enable_writing_status(struct spi_flash *flash)
 }
 
 static int
-sst_disable_writing(struct spi_flash *flash)
+sst_disable_writing(const struct spi_flash *flash)
 {
 	int ret = spi_flash_cmd(flash->spi, CMD_SST_WRDI, NULL, 0);
 	if (ret)
@@ -132,7 +134,7 @@ sst_disable_writing(struct spi_flash *flash)
 }
 
 static int
-sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
+sst_byte_write(const struct spi_flash *flash, u32 offset, const void *buf)
 {
 	int ret;
 	u8 cmd[4] = {
@@ -158,8 +160,8 @@ sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
 	return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
 }
 
-static int
-sst_write_256(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
+static int sst_write_256(const struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf)
 {
 	size_t actual, chunk_len, cmd_len;
 	unsigned long byte_addr;
@@ -170,8 +172,6 @@ sst_write_256(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
 	page_size = 256;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	/* If the data is not word aligned, write out leading single byte */
 	actual = offset % 2;
 	if (actual) {
@@ -234,15 +234,13 @@ done:
 	return ret;
 }
 
-static int
-sst_write_ai(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
+static int sst_write_ai(const struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf)
 {
 	size_t actual, cmd_len;
 	int ret = 0;
 	u8 cmd[4];
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	/* If the data is not word aligned, write out leading single byte */
 	actual = offset % 2;
 	if (actual) {
@@ -301,7 +299,7 @@ done:
 
 
 static int
-sst_unlock(struct spi_flash *flash)
+sst_unlock(const struct spi_flash *flash)
 {
 	int ret;
 	u8 cmd, status;
@@ -349,10 +347,10 @@ spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode)
 	stm->flash.spi = spi;
 	stm->flash.name = params->name;
 
-	stm->flash.write = params->write;
-	stm->flash.erase = spi_flash_cmd_erase;
-	stm->flash.status = spi_flash_cmd_status;
-	stm->flash.read = spi_flash_cmd_read_fast;
+	stm->flash.internal_write = params->write;
+	stm->flash.internal_erase = spi_flash_cmd_erase;
+	stm->flash.internal_status = spi_flash_cmd_status;
+	stm->flash.internal_read = spi_flash_cmd_read_fast;
 	stm->flash.sector_size = SST_SECTOR_SIZE;
 	stm->flash.size = stm->flash.sector_size * params->nr_sectors;
 	stm->flash.erase_cmd = CMD_SST_SE;
diff --git a/src/drivers/spi/stmicro.c b/src/drivers/spi/stmicro.c
index 6ab601c..5618162 100644
--- a/src/drivers/spi/stmicro.c
+++ b/src/drivers/spi/stmicro.c
@@ -19,8 +19,10 @@
  * GNU General Public License for more details.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -69,8 +71,8 @@ struct stmicro_spi_flash {
 	const struct stmicro_spi_flash_params *params;
 };
 
-static inline struct stmicro_spi_flash *to_stmicro_spi_flash(struct spi_flash
-							     *flash)
+static inline
+struct stmicro_spi_flash *to_stmicro_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct stmicro_spi_flash, flash);
 }
@@ -174,7 +176,7 @@ static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
 	},
 };
 
-static int stmicro_write(struct spi_flash *flash,
+static int stmicro_write(const struct spi_flash *flash,
 			 u32 offset, size_t len, const void *buf)
 {
 	struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
@@ -188,8 +190,6 @@ static int stmicro_write(struct spi_flash *flash,
 	page_size = stm->params->page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -232,7 +232,6 @@ static int stmicro_write(struct spi_flash *flash,
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -272,9 +271,9 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
 	stm.flash.spi = spi;
 	stm.flash.name = params->name;
 
-	stm.flash.write = stmicro_write;
-	stm.flash.erase = spi_flash_cmd_erase;
-	stm.flash.read = spi_flash_cmd_read_fast;
+	stm.flash.internal_write = stmicro_write;
+	stm.flash.internal_erase = spi_flash_cmd_erase;
+	stm.flash.internal_read = spi_flash_cmd_read_fast;
 	stm.flash.sector_size = params->page_size * params->pages_per_sector;
 	stm.flash.size = stm.flash.sector_size * params->nr_sectors;
 	stm.flash.erase_cmd = params->op_erase;
diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c
index defb8a5..c6af2ac 100644
--- a/src/drivers/spi/winbond.c
+++ b/src/drivers/spi/winbond.c
@@ -4,8 +4,10 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <console/console.h>
 #include <stdlib.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 
 #include "spi_flash_internal.h"
 
@@ -40,7 +42,7 @@ struct winbond_spi_flash {
 };
 
 static inline struct winbond_spi_flash *
-to_winbond_spi_flash(struct spi_flash *flash)
+to_winbond_spi_flash(const struct spi_flash *flash)
 {
 	return container_of(flash, struct winbond_spi_flash, flash);
 }
@@ -136,8 +138,8 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
 	},
 };
 
-static int winbond_write(struct spi_flash *flash,
-		u32 offset, size_t len, const void *buf)
+static int winbond_write(const struct spi_flash *flash, u32 offset, size_t len,
+			const void *buf)
 {
 	struct winbond_spi_flash *stm = to_winbond_spi_flash(flash);
 	unsigned long byte_addr;
@@ -150,8 +152,6 @@ static int winbond_write(struct spi_flash *flash,
 	page_size = 1 << stm->params->l2_page_size;
 	byte_addr = offset % page_size;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	for (actual = 0; actual < len; actual += chunk_len) {
 		chunk_len = min(len - actual, page_size - byte_addr);
 		chunk_len = spi_crop_chunk(sizeof(cmd), chunk_len);
@@ -224,13 +224,13 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
 	/* Assuming power-of-two page size initially. */
 	page_size = 1 << params->l2_page_size;
 
-	stm.flash.write = winbond_write;
-	stm.flash.erase = spi_flash_cmd_erase;
-	stm.flash.status = spi_flash_cmd_status;
+	stm.flash.internal_write = winbond_write;
+	stm.flash.internal_erase = spi_flash_cmd_erase;
+	stm.flash.internal_status = spi_flash_cmd_status;
 #if CONFIG_SPI_FLASH_NO_FAST_READ
-	stm.flash.read = spi_flash_cmd_read_slow;
+	stm.flash.internal_read = spi_flash_cmd_read_slow;
 #else
-	stm.flash.read = spi_flash_cmd_read_fast;
+	stm.flash.internal_read = spi_flash_cmd_read_fast;
 #endif
 	stm.flash.sector_size = (1 << stm.params->l2_page_size) *
 		stm.params->pages_per_sector;
diff --git a/src/include/spi-generic.h b/src/include/spi-generic.h
index 9097f48..ee02394 100644
--- a/src/include/spi-generic.h
+++ b/src/include/spi-generic.h
@@ -24,9 +24,6 @@
 #define SPI_OPCODE_WREN 0x06
 #define SPI_OPCODE_FAST_READ 0x0b
 
-#define SPI_READ_FLAG	0x01
-#define SPI_WRITE_FLAG	0x02
-
 /*-----------------------------------------------------------------------
  * Representation of a SPI slave, i.e. what we're communicating with.
  *
@@ -34,7 +31,6 @@
  *
  *   bus:	ID of the bus that the slave is attached to.
  *   cs:	ID of the chip select connected to the slave.
- *   rw: 	Read or Write flag
  *   max_transfer_size: maximum amount of bytes which can be sent in a single
  *              read or write transaction, usually this is a controller
  *              property, kept in the slave structure for convenience. Zero in
@@ -43,7 +39,6 @@
 struct spi_slave {
 	unsigned int	bus;
 	unsigned int	cs;
-	unsigned int	rw;
 	unsigned int	max_transfer_size;
 	int force_programmer_specific;
 	struct spi_flash * (*programmer_specific_probe) (struct spi_slave *spi);
diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h
index 52de184..5cb6829 100644
--- a/src/include/spi_flash.h
+++ b/src/include/spi_flash.h
@@ -17,35 +17,63 @@
 
 #include <stdint.h>
 #include <stddef.h>
-#include <console/console.h>
-#include <spi-generic.h>
 #include <boot/coreboot_tables.h>
 
 struct spi_flash {
 	struct spi_slave *spi;
-
-	const char	*name;
-
-	u32		size;
-
-	u32		sector_size;
-
-	u8		erase_cmd;
-
-	u8		status_cmd;
-
-	/* All callbacks return 0 on success and != 0 on error. */
-	int		(*read)(struct spi_flash *flash, u32 offset,
+	const char *name;
+	u32 size;
+	u32 sector_size;
+	u8 erase_cmd;
+	u8 status_cmd;
+	/*
+	 * Internal functions are expected to be called ONLY by spi flash
+	 * driver. External components should only use the public API calls
+	 * spi_flash_{read,write,erase,status,volatile_group_begin,
+	 * volatile_group_end}.
+	 */
+	int (*internal_read)(const struct spi_flash *flash, u32 offset,
 				size_t len, void *buf);
-	int		(*write)(struct spi_flash *flash, u32 offset,
+	int (*internal_write)(const struct spi_flash *flash, u32 offset,
 				size_t len, const void *buf);
-	int		(*erase)(struct spi_flash *flash, u32 offset,
+	int (*internal_erase)(const struct spi_flash *flash, u32 offset,
 				size_t len);
-	int		(*status)(struct spi_flash *flash, u8 *reg);
+	int (*internal_status)(const struct spi_flash *flash, u8 *reg);
 };
 
+void lb_spi_flash(struct lb_header *header);
+
+/* SPI Flash Driver Public API */
 struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs);
 
-void lb_spi_flash(struct lb_header *header);
+/* All the following functions return 0 on success and non-zero on error. */
+int spi_flash_read(const struct spi_flash *flash, u32 offset, size_t len,
+		   void *buf);
+int spi_flash_write(const struct spi_flash *flash, u32 offset, size_t len,
+		    const void *buf);
+int spi_flash_erase(const struct spi_flash *flash, u32 offset, size_t len);
+int spi_flash_status(const struct spi_flash *flash, u8 *reg);
+/*
+ * Some SPI controllers require exclusive access to SPI flash when volatile
+ * operations like erase or write are being performed. In such cases,
+ * volatile_group_begin will gain exclusive access to SPI flash if not already
+ * acquired and volatile_group_end will end exclusive access if this was the
+ * last request in the group. spi_flash_{write,erase} operations call
+ * volatile_group_begin at the start of function and volatile_group_end after
+ * erase/write operation is performed. These functions can also be used by any
+ * components that wish to club multiple volatile operations into a single
+ * group.
+ */
+int spi_flash_volatile_group_begin(const struct spi_flash *flash);
+int spi_flash_volatile_group_end(const struct spi_flash *flash);
+
+/*
+ * These are callbacks for marking the start and end of volatile group as
+ * handled by the chipset. Not every chipset requires this special handling. So,
+ * these functions are expected to be implemented in Kconfig option for volatile
+ * group is enabled (SPI_FLASH_HAS_VOLATILE_GROUP).
+ */
+int chipset_volatile_group_begin(const struct spi_flash *flash);
+int chipset_volatile_group_end(const struct spi_flash *flash);
 
 #endif /* _SPI_FLASH_H_ */
diff --git a/src/northbridge/amd/agesa/oem_s3.c b/src/northbridge/amd/agesa/oem_s3.c
index fcf8ada..c7d23ff 100644
--- a/src/northbridge/amd/agesa/oem_s3.c
+++ b/src/northbridge/amd/agesa/oem_s3.c
@@ -97,15 +97,13 @@ static int spi_SaveS3info(u32 pos, u32 size, u8 *buf, u32 len)
 	if (!flash)
 		return -1;
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-	spi_claim_bus(flash->spi);
+	spi_flash_volatile_group_begin(flash);
 
-	flash->erase(flash, pos, size);
-	flash->write(flash, pos, sizeof(len), &len);
-	flash->write(flash, pos + sizeof(len), len, buf);
+	spi_flash_erase(flash, pos, size);
+	spi_flash_write(flash, pos, sizeof(len), &len);
+	spi_flash_write(flash, pos + sizeof(len), len, buf);
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-	spi_release_bus(flash->spi);
+	spi_flash_volatile_group_end(flash);
 	return 0;
 #else
 	return -1;
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
index 97cadcb..d3fc53b 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
@@ -1140,20 +1140,17 @@ int8_t save_mct_information_to_nvram(void)
 		return -1;
 	}
 
-	/* Set up SPI flash access */
-	flash->spi->rw = SPI_WRITE_FLAG;
-	spi_claim_bus(flash->spi);
+	spi_flash_volatile_group_begin(flash);
 
 	/* Erase and write data structure */
-	flash->erase(flash, s3nv_offset, CONFIG_S3_DATA_SIZE);
-	flash->write(flash, s3nv_offset, sizeof(struct amd_s3_persistent_data), persistent_data);
+	spi_flash_erase(flash, s3nv_offset, CONFIG_S3_DATA_SIZE);
+	spi_flash_write(flash, s3nv_offset,
+			sizeof(struct amd_s3_persistent_data), persistent_data);
 
 	/* Deallocate temporary data structures */
 	free(persistent_data);
 
-	/* Tear down SPI flash access */
-	flash->spi->rw = SPI_WRITE_FLAG;
-	spi_release_bus(flash->spi);
+	spi_flash_volatile_group_end(flash);
 
 	/* Allow training bypass if DIMM configuration is unchanged on next boot */
 	nvram = 1;
diff --git a/src/northbridge/intel/common/mrc_cache.c b/src/northbridge/intel/common/mrc_cache.c
index 4c3ee5d..a158123 100644
--- a/src/northbridge/intel/common/mrc_cache.c
+++ b/src/northbridge/intel/common/mrc_cache.c
@@ -212,7 +212,8 @@ static void update_mrc_cache(void *unused)
 		       "Need to erase the MRC cache region of %d bytes at %p\n",
 		       cache_size, cache_base);
 
-		flash->erase(flash, to_flash_offset(flash, cache_base), cache_size);
+		spi_flash_erase(flash, to_flash_offset(flash, cache_base),
+				cache_size);
 
 		/* we will start at the beginning again */
 		cache = cache_base;
@@ -220,8 +221,8 @@ static void update_mrc_cache(void *unused)
 	//  4. write mrc data with flash->write()
 	printk(BIOS_DEBUG, "Finally: write MRC cache update to flash at %p\n",
 	       cache);
-	ret = flash->write(flash, to_flash_offset(flash, cache),
-		     current->mrc_data_size + sizeof(*current), current);
+	ret = spi_flash_write(flash, to_flash_offset(flash, cache),
+			current->mrc_data_size + sizeof(*current), current);
 
 	if (ret)
 		printk(BIOS_WARNING, "Writing the MRC cache failed with ret %d\n",
diff --git a/src/soc/imgtec/pistachio/spi.c b/src/soc/imgtec/pistachio/spi.c
index f38607c..87dd66f 100644
--- a/src/soc/imgtec/pistachio/spi.c
+++ b/src/soc/imgtec/pistachio/spi.c
@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  */
 
+#include <console/console.h>
 #include <soc/cpu.h>
 #include <soc/spi.h>
 #include <spi_flash.h>
@@ -444,7 +445,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
 	img_slave->base = base;
 	slave->bus = bus;
 	slave->cs = cs;
-	slave->rw = SPI_READ_FLAG | SPI_WRITE_FLAG;
 	slave->max_transfer_size = IMGTEC_SPI_MAX_TRANSFER_SIZE;
 
 	device_parameters->bitrate = 64;
diff --git a/src/soc/intel/apollolake/spi.c b/src/soc/intel/apollolake/spi.c
index d60176a..018572c 100644
--- a/src/soc/intel/apollolake/spi.c
+++ b/src/soc/intel/apollolake/spi.c
@@ -19,12 +19,14 @@
 
 #include <arch/early_variables.h>
 #include <arch/io.h>
+#include <console/console.h>
 #include <device/device.h>
 #include <device/pci.h>
 #include <soc/intel/common/spi.h>
 #include <soc/pci_devs.h>
 #include <soc/spi.h>
 #include <spi_flash.h>
+#include <spi-generic.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -224,7 +226,8 @@ void spi_release_bus(struct spi_slave *slave)
 	/* No magic needed here. */
 }
 
-static int nuclear_spi_erase(struct spi_flash *flash, uint32_t offset, size_t len)
+static int nuclear_spi_erase(const struct spi_flash *flash, uint32_t offset,
+			     size_t len)
 {
 	int ret;
 	size_t erase_size;
@@ -274,7 +277,8 @@ static size_t get_xfer_len(uint32_t addr, size_t len)
 	return xfer_len;
 }
 
-static int nuclear_spi_read(struct spi_flash *flash, uint32_t addr, size_t len, void *buf)
+static int nuclear_spi_read(const struct spi_flash *flash, uint32_t addr,
+			size_t len, void *buf)
 {
 	int ret;
 	size_t xfer_len;
@@ -300,8 +304,8 @@ static int nuclear_spi_read(struct spi_flash *flash, uint32_t addr, size_t len,
 	return SUCCESS;
 }
 
-static int nuclear_spi_write(struct spi_flash *flash,
-			   uint32_t addr, size_t len, const void *buf)
+static int nuclear_spi_write(const struct spi_flash *flash, uint32_t addr,
+			size_t len, const void *buf)
 {
 	int ret;
 	size_t xfer_len;
@@ -326,7 +330,7 @@ static int nuclear_spi_write(struct spi_flash *flash,
 	return SUCCESS;
 }
 
-static int nuclear_spi_status(struct spi_flash *flash, uint8_t *reg)
+static int nuclear_spi_status(const struct spi_flash *flash, uint8_t *reg)
 {
 	int ret;
 	BOILERPLATE_CREATE_CTX(ctx);
@@ -379,10 +383,10 @@ static struct spi_flash *nuclear_flash_probe(struct spi_slave *spi)
 	 * flash->status_cmd = ???
 	 */
 
-	flash->write = nuclear_spi_write;
-	flash->erase = nuclear_spi_erase;
-	flash->read = nuclear_spi_read;
-	flash->status = nuclear_spi_status;
+	flash->internal_write = nuclear_spi_write;
+	flash->internal_erase = nuclear_spi_erase;
+	flash->internal_read = nuclear_spi_read;
+	flash->internal_status = nuclear_spi_status;
 
 	return flash;
 }
diff --git a/src/soc/intel/baytrail/spi.c b/src/soc/intel/baytrail/spi.c
index ffe6198..d651350 100644
--- a/src/soc/intel/baytrail/spi.c
+++ b/src/soc/intel/baytrail/spi.c
@@ -21,7 +21,7 @@
 #include <arch/io.h>
 #include <console/console.h>
 #include <device/pci_ids.h>
-#include <spi_flash.h>
+#include <spi-generic.h>
 
 #include <soc/lpc.h>
 #include <soc/pci_devs.h>
diff --git a/src/soc/intel/braswell/spi.c b/src/soc/intel/braswell/spi.c
index 01fe773..97ca2e5 100644
--- a/src/soc/intel/braswell/spi.c
+++ b/src/soc/intel/braswell/spi.c
@@ -22,7 +22,7 @@
 #include <rules.h>
 #include <soc/lpc.h>
 #include <soc/pci_devs.h>
-#include <spi_flash.h>
+#include <spi-generic.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/soc/intel/common/nvm.c b/src/soc/intel/common/nvm.c
index 6b86faf..e44fb94 100644
--- a/src/soc/intel/common/nvm.c
+++ b/src/soc/intel/common/nvm.c
@@ -81,7 +81,7 @@ int nvm_erase(void *start, size_t size)
 {
 	if (nvm_init() < 0)
 		return -1;
-	return flash->erase(flash, nvm_mmio_to_flash_offset(start), size);
+	return spi_flash_erase(flash, nvm_mmio_to_flash_offset(start), size);
 }
 
 /* Write data to NVM. Returns 0 on success < 0 on error.  */
@@ -89,7 +89,8 @@ int nvm_write(void *start, const void *data, size_t size)
 {
 	if (nvm_init() < 0)
 		return -1;
-	return flash->write(flash, nvm_mmio_to_flash_offset(start), size, data);
+	return spi_flash_write(flash, nvm_mmio_to_flash_offset(start), size,
+				data);
 }
 
 /* Read flash status register to determine if write protect is active */
@@ -107,7 +108,7 @@ int nvm_is_write_protected(void)
 		wp_gpio = get_write_protect_state();
 
 		/* Read Status Register 1 */
-		if (flash->status(flash, &sr1) < 0) {
+		if (spi_flash_status(flash, &sr1) < 0) {
 			printk(BIOS_ERR,
 				"Failed to read SPI status register 1\n");
 			return -1;
diff --git a/src/soc/intel/fsp_baytrail/nvm.c b/src/soc/intel/fsp_baytrail/nvm.c
index ed0d121..6f2fdbb 100644
--- a/src/soc/intel/fsp_baytrail/nvm.c
+++ b/src/soc/intel/fsp_baytrail/nvm.c
@@ -67,7 +67,7 @@ int nvm_erase(void *start, size_t size)
 {
 	if (nvm_init() < 0)
 		return -1;
-	flash->erase(flash, to_flash_offset(start), size);
+	spi_flash_erase(flash, to_flash_offset(start), size);
 	return 0;
 }
 
@@ -76,6 +76,6 @@ int nvm_write(void *start, const void *data, size_t size)
 {
 	if (nvm_init() < 0)
 		return -1;
-	flash->write(flash, to_flash_offset(start), size, data);
+	spi_flash_write(flash, to_flash_offset(start), size, data);
 	return 0;
 }
diff --git a/src/soc/intel/fsp_baytrail/spi.c b/src/soc/intel/fsp_baytrail/spi.c
index 374e7f6..b021175 100644
--- a/src/soc/intel/fsp_baytrail/spi.c
+++ b/src/soc/intel/fsp_baytrail/spi.c
@@ -22,7 +22,7 @@
 #include <arch/io.h>
 #include <console/console.h>
 #include <device/pci_ids.h>
-#include <spi_flash.h>
+#include <spi-generic.h>
 
 #include <soc/lpc.h>
 #include <soc/pci_devs.h>
diff --git a/src/soc/intel/skylake/flash_controller.c b/src/soc/intel/skylake/flash_controller.c
index 56c7c69..5a715a4 100644
--- a/src/soc/intel/skylake/flash_controller.c
+++ b/src/soc/intel/skylake/flash_controller.c
@@ -18,12 +18,12 @@
 #include <stdlib.h>
 #include <string.h>
 #include <bootstate.h>
-#include <spi_flash.h>
 #include <timer.h>
 #include <soc/flash_controller.h>
 #include <soc/intel/common/spi.h>
 #include <soc/pci_devs.h>
 #include <soc/spi.h>
+#include <spi-generic.h>
 
 static inline uint16_t spi_read_hsfs(pch_spi_regs * const regs)
 {
@@ -181,7 +181,7 @@ void spi_release_bus(struct spi_slave *slave)
 	/* Handled by PCH automatically. */
 }
 
-int pch_hwseq_erase(struct spi_flash *flash, u32 offset, size_t len)
+int pch_hwseq_erase(const struct spi_flash *flash, u32 offset, size_t len)
 {
 	u32 start, end, erase_size;
 	int ret = 0;
@@ -192,8 +192,6 @@ int pch_hwseq_erase(struct spi_flash *flash, u32 offset, size_t len)
 		return -1;
 	}
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	start = offset;
 	end = start + len;
 
@@ -231,7 +229,8 @@ static void pch_read_data(uint8_t *data, int len)
 	}
 }
 
-int pch_hwseq_read(struct spi_flash *flash, u32 addr, size_t len, void *buf)
+int pch_hwseq_read(const struct spi_flash *flash, u32 addr, size_t len,
+		void *buf)
 {
 	uint8_t block_len;
 
@@ -292,8 +291,8 @@ static void pch_fill_data(const uint8_t *data, int len)
 		writel_(temp32, (uint8_t *)spi_bar->fdata + (i - (i % 4)));
 }
 
-int pch_hwseq_write(struct spi_flash *flash,
-			   u32 addr, size_t len, const void *buf)
+int pch_hwseq_write(const struct spi_flash *flash, u32 addr, size_t len,
+		const void *buf)
 {
 	uint8_t block_len;
 	uint32_t start = addr;
@@ -330,7 +329,7 @@ int pch_hwseq_write(struct spi_flash *flash,
 	return 0;
 }
 
-int pch_hwseq_read_status(struct spi_flash *flash, u8 *reg)
+int pch_hwseq_read_status(const struct spi_flash *flash, u8 *reg)
 {
 	size_t block_len = SPI_READ_STATUS_LENGTH;
 	const int timeout_ms = 6;
@@ -358,10 +357,10 @@ static struct spi_flash *spi_flash_hwseq_probe(struct spi_slave *spi)
 	flash->spi = spi;
 	flash->name = "Opaque HW-sequencing";
 
-	flash->write = pch_hwseq_write;
-	flash->erase = pch_hwseq_erase;
-	flash->read = pch_hwseq_read;
-	flash->status = pch_hwseq_read_status;
+	flash->internal_write = pch_hwseq_write;
+	flash->internal_erase = pch_hwseq_erase;
+	flash->internal_read = pch_hwseq_read;
+	flash->internal_status = pch_hwseq_read_status;
 
 	/* The hardware sequencing supports 4KiB or 64KiB erase. Use 4KiB. */
 	flash->sector_size = 4*KiB;
diff --git a/src/soc/intel/skylake/include/soc/flash_controller.h b/src/soc/intel/skylake/include/soc/flash_controller.h
index 49d60b0..0050067 100644
--- a/src/soc/intel/skylake/include/soc/flash_controller.h
+++ b/src/soc/intel/skylake/include/soc/flash_controller.h
@@ -21,13 +21,12 @@
 #include <console/console.h>
 #include <spi_flash.h>
 
-int pch_hwseq_erase(struct spi_flash *flash, u32 offset, size_t len);
-int pch_hwseq_write(struct spi_flash *flash,
-			   u32 addr, size_t len, const void *buf);
-
-int pch_hwseq_read(struct spi_flash *flash,
-			  u32 addr, size_t len, void *buf);
-int pch_hwseq_read_status(struct spi_flash *flash, u8 *reg);
+int pch_hwseq_erase(const struct spi_flash *flash, u32 offset, size_t len);
+int pch_hwseq_write(const struct spi_flash *flash, u32 addr, size_t len,
+		const void *buf);
+int pch_hwseq_read(const struct spi_flash *flash, u32 addr, size_t len,
+		void *buf);
+int pch_hwseq_read_status(const struct spi_flash *flash, u8 *reg);
 
 
 #if IS_ENABLED(CONFIG_DEBUG_SPI_FLASH)
diff --git a/src/soc/intel/skylake/romstage/spi.c b/src/soc/intel/skylake/romstage/spi.c
index db69cbe..1622404 100644
--- a/src/soc/intel/skylake/romstage/spi.c
+++ b/src/soc/intel/skylake/romstage/spi.c
@@ -16,6 +16,7 @@
 
 #include <soc/flash_controller.h>
 #include <soc/romstage.h>
+#include <spi-generic.h>
 
 /*
  * Minimal set of commands to read WPSR from SPI.
diff --git a/src/soc/mediatek/mt8173/flash_controller.c b/src/soc/mediatek/mt8173/flash_controller.c
index 5d73f3a..dc64d40 100644
--- a/src/soc/mediatek/mt8173/flash_controller.c
+++ b/src/soc/mediatek/mt8173/flash_controller.c
@@ -157,7 +157,8 @@ static int pio_read(u32 addr, u8 *buf, u32 len)
 	return 0;
 }
 
-static int nor_read(struct spi_flash *flash, u32 addr, size_t len, void *buf)
+static int nor_read(const struct spi_flash *flash, u32 addr, size_t len,
+		void *buf)
 {
 	u32 next;
 
@@ -195,8 +196,8 @@ static int nor_read(struct spi_flash *flash, u32 addr, size_t len, void *buf)
 	return 0;
 }
 
-static int nor_write(struct spi_flash *flash, u32 addr, size_t len,
-		       const void *buf)
+static int nor_write(const struct spi_flash *flash, u32 addr, size_t len,
+		const void *buf)
 {
 	const u8 *buffer = (const u8 *)buf;
 
@@ -214,7 +215,7 @@ static int nor_write(struct spi_flash *flash, u32 addr, size_t len,
 	return 0;
 }
 
-static int nor_erase(struct spi_flash *flash, u32 offset, size_t len)
+static int nor_erase(const struct spi_flash *flash, u32 offset, size_t len)
 {
 	int sector_start = offset;
 	int sector_num = (u32)len / flash->sector_size;
@@ -242,10 +243,10 @@ struct spi_flash *mt8173_nor_flash_probe(struct spi_slave *spi)
 	write32(&mt8173_nor->wrprot, SFLASH_COMMAND_ENABLE);
 	flash.spi = spi;
 	flash.name = "mt8173 flash controller";
-	flash.write = nor_write;
-	flash.erase = nor_erase;
-	flash.read = nor_read;
-	flash.status = 0;
+	flash.internal_write = nor_write;
+	flash.internal_erase = nor_erase;
+	flash.internal_read = nor_read;
+	flash.internal_status = 0;
 	flash.sector_size = 0x1000;
 	flash.erase_cmd = SECTOR_ERASE_CMD;
 	flash.size = CONFIG_ROM_SIZE;
diff --git a/src/soc/qualcomm/ipq40xx/include/soc/spi.h b/src/soc/qualcomm/ipq40xx/include/soc/spi.h
index 014b333..1fd6a57 100644
--- a/src/soc/qualcomm/ipq40xx/include/soc/spi.h
+++ b/src/soc/qualcomm/ipq40xx/include/soc/spi.h
@@ -32,9 +32,9 @@
 #ifndef _IPQ40XX_SPI_H_
 #define _IPQ40XX_SPI_H_
 
-#include <spi_flash.h>
 #include <soc/iomap.h>
 #include <soc/qup.h>
+#include <spi-generic.h>
 
 #define BLSP0_QUP_REG_BASE		((void *)0x78b5000u)
 #define BLSP1_QUP_REG_BASE		((void *)0x78b6000u)
diff --git a/src/soc/qualcomm/ipq40xx/spi.c b/src/soc/qualcomm/ipq40xx/spi.c
index 8d39f77..dcd00c0 100644
--- a/src/soc/qualcomm/ipq40xx/spi.c
+++ b/src/soc/qualcomm/ipq40xx/spi.c
@@ -28,6 +28,7 @@
  */
 
 #include <arch/io.h>
+#include <console/console.h>
 #include <delay.h>
 #include <gpio.h>
 #include <soc/iomap.h>
diff --git a/src/soc/qualcomm/ipq806x/include/soc/spi.h b/src/soc/qualcomm/ipq806x/include/soc/spi.h
index 3e62346..4f6f055 100644
--- a/src/soc/qualcomm/ipq806x/include/soc/spi.h
+++ b/src/soc/qualcomm/ipq806x/include/soc/spi.h
@@ -6,7 +6,7 @@
 #ifndef _IPQ806X_SPI_H_
 #define _IPQ806X_SPI_H_
 
-#include <spi_flash.h>
+#include <spi-generic.h>
 #include <soc/iomap.h>
 
 #define QUP5_BASE	((uint32_t)GSBI_QUP5_BASE)
diff --git a/src/soc/qualcomm/ipq806x/spi.c b/src/soc/qualcomm/ipq806x/spi.c
index 4a35313..71a8c29 100644
--- a/src/soc/qualcomm/ipq806x/spi.c
+++ b/src/soc/qualcomm/ipq806x/spi.c
@@ -3,6 +3,7 @@
  */
 
 #include <arch/io.h>
+#include <console/console.h>
 #include <delay.h>
 #include <gpio.h>
 #include <soc/iomap.h>
diff --git a/src/soc/rockchip/common/spi.c b/src/soc/rockchip/common/spi.c
index 57e9ca1..3666de3 100644
--- a/src/soc/rockchip/common/spi.c
+++ b/src/soc/rockchip/common/spi.c
@@ -37,45 +37,30 @@ struct rockchip_spi_slave {
 
 static struct rockchip_spi_slave rockchip_spi_slaves[] = {
 	{
-	 .slave = {
-		   .bus = 0,
-		   .rw = SPI_READ_FLAG | SPI_WRITE_FLAG,
-		   },
+	 .slave = { .bus = 0, },
 	 .regs = (void *)SPI0_BASE,
 	},
 	{
-	 .slave = {.bus = 1, .rw = SPI_READ_FLAG,},
+	 .slave = { .bus = 1, },
 	 .regs = (void *)SPI1_BASE,
 	},
 	{
-	 .slave = {
-		   .bus = 2,
-		   .rw = SPI_READ_FLAG | SPI_WRITE_FLAG,
-		   },
+	 .slave = { .bus = 2, },
 	 .regs = (void *)SPI2_BASE,
 	},
 #ifdef SPI3_BASE
 	{
-	 .slave = {
-		   .bus = 3,
-		   .rw = SPI_READ_FLAG | SPI_WRITE_FLAG,
-		   },
+	 .slave = { .bus = 3, },
 	 .regs = (void *)SPI3_BASE,
 	},
 #ifdef SPI4_BASE
 	{
-	 .slave = {
-		   .bus = 4,
-		   .rw = SPI_READ_FLAG | SPI_WRITE_FLAG,
-		   },
+	 .slave = { .bus = 4, },
 	 .regs = (void *)SPI4_BASE,
 	},
 #ifdef SPI5_BASE
 	{
-	 .slave = {
-		   .bus = 5,
-		   .rw = SPI_READ_FLAG | SPI_WRITE_FLAG,
-		   },
+	 .slave = { .bus = 5, },
 	 .regs = (void *)SPI5_BASE,
 	},
 #endif
diff --git a/src/soc/samsung/exynos5420/spi.c b/src/soc/samsung/exynos5420/spi.c
index 589809d..fd31a2f 100644
--- a/src/soc/samsung/exynos5420/spi.c
+++ b/src/soc/samsung/exynos5420/spi.c
@@ -19,7 +19,7 @@
 #include <console/console.h>
 #include <soc/cpu.h>
 #include <soc/spi.h>
-#include <spi_flash.h>
+#include <spi-generic.h>
 #include <stdlib.h>
 #include <string.h>
 #include <symbols.h>
@@ -48,13 +48,12 @@ static struct exynos_spi_slave exynos_spi_slaves[3] = {
 	},
 	// SPI 1
 	{
-		.slave = { .bus = 1, .rw = SPI_READ_FLAG, },
+		.slave = { .bus = 1, },
 		.regs = (void *)EXYNOS5_SPI1_BASE,
 	},
 	// SPI 2
 	{
-		.slave = { .bus = 2,
-			   .rw = SPI_READ_FLAG | SPI_WRITE_FLAG, },
+		.slave = { .bus = 2, },
 		.regs = (void *)EXYNOS5_SPI2_BASE,
 	},
 };
diff --git a/src/southbridge/amd/agesa/hudson/Kconfig b/src/southbridge/amd/agesa/hudson/Kconfig
index fd79a3f..eed83ae 100644
--- a/src/southbridge/amd/agesa/hudson/Kconfig
+++ b/src/southbridge/amd/agesa/hudson/Kconfig
@@ -64,6 +64,7 @@ config HUDSON_XHCI_FWM
 config HUDSON_IMC_FWM
 	bool "Add imc firmware"
 	default y if USE_BLOBS
+	select SPI_FLASH_HAS_VOLATILE_GROUP if SPI_FLASH
 	help
 	  Add Hudson 2/3/4 IMC Firmware to support the onboard fan control
 
diff --git a/src/southbridge/amd/agesa/hudson/spi.c b/src/southbridge/amd/agesa/hudson/spi.c
index 31160bd..f1a82e9 100644
--- a/src/southbridge/amd/agesa/hudson/spi.c
+++ b/src/southbridge/amd/agesa/hudson/spi.c
@@ -17,17 +17,14 @@
 #include <string.h>
 #include <arch/io.h>
 #include <console/console.h>
+#include <spi_flash.h>
 #include <spi-generic.h>
 #include <device/device.h>
 #include <device/pci.h>
 #include <device/pci_ops.h>
 
-#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
 #include <Proc/Fch/FchPlatform.h>
 
-static int bus_claimed = 0;
-#endif
-
 #define SPI_REG_OPCODE		0x0
 #define SPI_REG_CNTRL01		0x1
 #define SPI_REG_CNTRL02		0x2
@@ -149,32 +146,34 @@ int spi_xfer(struct spi_slave *slave, const void *dout,
 
 	return 0;
 }
+
 int spi_claim_bus(struct spi_slave *slave)
 {
-#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
+	/* Nothing is required. */
+	return 0;
+}
 
-	if (slave->rw == SPI_WRITE_FLAG) {
-		bus_claimed++;
-		if (bus_claimed == 1)
-			ImcSleep(NULL);
-	}
-#endif
+void spi_release_bus(struct spi_slave *slave)
+{
+	/* Nothing is required. */
+}
+
+int chipset_volatile_group_begin(const struct spi_flash *flash)
+{
+	if (!IS_ENABLED (CONFIG_HUDSON_IMC_FWM))
+		return 0;
 
+	ImcSleep(NULL);
 	return 0;
 }
 
-void spi_release_bus(struct spi_slave *slave)
+int chipset_volatile_group_end(const struct spi_flash *flash)
 {
-#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
-
-	if (slave->rw == SPI_WRITE_FLAG)  {
-		bus_claimed--;
-		if (bus_claimed <= 0) {
-			bus_claimed = 0;
-			ImcWakeup(NULL);
-		}
-	}
-#endif
+	if (!IS_ENABLED (CONFIG_HUDSON_IMC_FWM))
+		return 0;
+
+	ImcWakeup(NULL);
+	return 0;
 }
 
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
diff --git a/src/southbridge/amd/cimx/sb800/Kconfig b/src/southbridge/amd/cimx/sb800/Kconfig
index a48b93a..07232f1 100644
--- a/src/southbridge/amd/cimx/sb800/Kconfig
+++ b/src/southbridge/amd/cimx/sb800/Kconfig
@@ -123,6 +123,7 @@ endif
 config SB800_IMC_FWM
 	bool "Add IMC firmware"
 	default n
+	select SPI_FLASH_HAS_VOLATILE_GROUP if SPI_FLASH
 	help
 	  Add SB800 / Hudson 1 IMC Firmware to support the onboard fan control.
 
diff --git a/src/southbridge/amd/cimx/sb800/spi.c b/src/southbridge/amd/cimx/sb800/spi.c
index f689892..726d8e0 100644
--- a/src/southbridge/amd/cimx/sb800/spi.c
+++ b/src/southbridge/amd/cimx/sb800/spi.c
@@ -17,18 +17,15 @@
 #include <string.h>
 #include <arch/io.h>
 #include <console/console.h>
+#include <spi_flash.h>
 #include <spi-generic.h>
 #include <device/device.h>
 #include <device/pci.h>
 #include <device/pci_ops.h>
 
-#if IS_ENABLED (CONFIG_SB800_IMC_FWM)
 #include "SBPLATFORM.h"
 #include <vendorcode/amd/cimx/sb800/ECfan.h>
 
-static int bus_claimed = 0;
-#endif
-
 #define AMD_SB_SPI_TX_LEN	8
 
 static uintptr_t spibar;
@@ -114,8 +111,6 @@ int spi_xfer(struct spi_slave *slave, const void *dout,
 	return 0;
 }
 
-#if IS_ENABLED (CONFIG_SB800_IMC_FWM)
-
 static void ImcSleep(void)
 {
 	u8	cmd_val = 0x96;		/* Kick off IMC Mailbox command 96 */
@@ -142,34 +137,35 @@ static void ImcWakeup(void)
 
 	WaitForEcLDN9MailboxCmdAck();
 }
-#endif
 
 int spi_claim_bus(struct spi_slave *slave)
 {
-#if IS_ENABLED (CONFIG_SB800_IMC_FWM)
+	/* Nothing is required. */
+	return 0;
+}
 
-	if (slave->rw == SPI_WRITE_FLAG) {
-		bus_claimed++;
-		if (bus_claimed == 1)
-			ImcSleep();
-	}
-#endif
+void spi_release_bus(struct spi_slave *slave)
+{
+	/* Nothing is required. */
+	return;
+}
+
+int chipset_volatile_group_begin(const struct spi_flash *flash)
+{
+	if (!IS_ENABLED(CONFIG_SB800_IMC_FWM))
+		return 0;
 
+	ImcSleep();
 	return 0;
 }
 
-void spi_release_bus(struct spi_slave *slave)
+int chipset_volatile_group_end(const struct spi_flash *flash)
 {
-#if IS_ENABLED (CONFIG_SB800_IMC_FWM)
-
-	if (slave->rw == SPI_WRITE_FLAG)  {
-		bus_claimed--;
-		if (bus_claimed <= 0) {
-			bus_claimed = 0;
-			ImcWakeup();
-		}
-	}
-#endif
+	if (!IS_ENABLED(CONFIG_SB800_IMC_FWM))
+		return 0;
+
+	ImcWakeup();
+	return 0;
 }
 
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
diff --git a/src/southbridge/intel/common/spi.c b/src/southbridge/intel/common/spi.c
index 1ab151b..e041422 100644
--- a/src/southbridge/intel/common/spi.c
+++ b/src/southbridge/intel/common/spi.c
@@ -737,7 +737,8 @@ static int ich_hwseq_wait_for_cycle_complete(unsigned int timeout,
 }
 
 
-static int ich_hwseq_erase(struct spi_flash *flash, u32 offset, size_t len)
+static int ich_hwseq_erase(const struct spi_flash *flash, u32 offset,
+			size_t len)
 {
 	u32 start, end, erase_size;
 	int ret;
@@ -750,7 +751,6 @@ static int ich_hwseq_erase(struct spi_flash *flash, u32 offset, size_t len)
 		return -1;
 	}
 
-	flash->spi->rw = SPI_WRITE_FLAG;
 	ret = spi_claim_bus(flash->spi);
 	if (ret) {
 		printk(BIOS_ERR, "SF: Unable to claim SPI bus\n");
@@ -801,8 +801,8 @@ static void ich_read_data(uint8_t *data, int len)
 	}
 }
 
-static int ich_hwseq_read(struct spi_flash *flash,
-			  u32 addr, size_t len, void *buf)
+static int ich_hwseq_read(const struct spi_flash *flash, u32 addr, size_t len,
+			void *buf)
 {
 	uint16_t hsfc;
 	uint16_t timeout = 100 * 60;
@@ -869,8 +869,8 @@ static void ich_fill_data(const uint8_t *data, int len)
 		writel_(temp32, cntlr.data + (i - (i % 4)));
 }
 
-static int ich_hwseq_write(struct spi_flash *flash,
-			   u32 addr, size_t len, const void *buf)
+static int ich_hwseq_write(const struct spi_flash *flash, u32 addr, size_t len,
+			const void *buf)
 {
 	uint16_t hsfc;
 	uint16_t timeout = 100 * 60;
@@ -934,9 +934,9 @@ static struct spi_flash *spi_flash_hwseq(struct spi_slave *spi)
 	flash->spi = spi;
 	flash->name = "Opaque HW-sequencing";
 
-	flash->write = ich_hwseq_write;
-	flash->erase = ich_hwseq_erase;
-	flash->read = ich_hwseq_read;
+	flash->internal_write = ich_hwseq_write;
+	flash->internal_erase = ich_hwseq_erase;
+	flash->internal_read = ich_hwseq_read;
 	ich_hwseq_set_addr (0);
 	switch ((cntlr.hsfs >> 3) & 3)
 	{



More information about the coreboot-gerrit mailing list