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@chromium.org>
Reviewed-on: https://review.coreboot.org/17462
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
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 @@
 		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 @@
 		       "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 @@
 	  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 @@
 };
 
 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 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 @@
 	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 @@
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -177,12 +171,12 @@
 	/* 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 @@
 };
 
 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 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 @@
 	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 @@
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -159,12 +153,12 @@
 	/* 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 @@
 };
 
 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 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 @@
 	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 @@
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -204,12 +198,12 @@
 	/* 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 @@
 	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 @@
 	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 @@
 	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 @@
 
 	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_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_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 @@
 	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 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 @@
 	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 @@
 	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 @@
 };
 
 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 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 @@
 	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 @@
 	/* 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 @@
 	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 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 @@
 	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 @@
 	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 @@
 	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 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 @@
 	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 @@
 	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 @@
 	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 @@
 					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 @@
 					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 @@
 	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 @@
 		return -1;
 	}
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	cmd[0] = flash->erase_cmd;
 	start = offset;
 	end = start + len;
@@ -240,7 +241,7 @@
 	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 @@
 };
 #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 @@
 		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 @@
 	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 @@
 		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 @@
 	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 @@
 	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 @@
 	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 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 @@
 }
 
 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 @@
 }
 
 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 @@
 }
 
 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 @@
 	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 @@
 	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 @@
 	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 @@
 
 
 static int
-sst_unlock(struct spi_flash *flash)
+sst_unlock(const struct spi_flash *flash)
 {
 	int ret;
 	u8 cmd, status;
@@ -349,10 +347,10 @@
 	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 @@
 	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 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 @@
 	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 @@
 	ret = 0;
 
 out:
-	spi_release_bus(flash->spi);
 	return ret;
 }
 
@@ -272,9 +271,9 @@
 	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 @@
 };
 
 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 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 @@
 	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 @@
 	/* 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 @@
 	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 @@
 		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 @@
 		       "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 @@
 	//  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 @@
 	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 @@
 	/* 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 @@
 	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 @@
 	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 @@
 	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 @@
 	 * 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 @@
 {
 	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 @@
 {
 	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 @@
 		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 @@
 {
 	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 @@
 {
 	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 @@
 	/* 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 @@
 		return -1;
 	}
 
-	flash->spi->rw = SPI_WRITE_FLAG;
-
 	start = offset;
 	end = start + len;
 
@@ -231,7 +229,8 @@
 	}
 }
 
-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 @@
 		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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 
 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 @@
 	},
 	// 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_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 @@
 
 	return 0;
 }
+
 int spi_claim_bus(struct spi_slave *slave)
 {
-#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
-
-	if (slave->rw == SPI_WRITE_FLAG) {
-		bus_claimed++;
-		if (bus_claimed == 1)
-			ImcSleep(NULL);
-	}
-#endif
-
+	/* Nothing is required. */
 	return 0;
 }
 
 void spi_release_bus(struct spi_slave *slave)
 {
-#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
+	/* Nothing is required. */
+}
 
-	if (slave->rw == SPI_WRITE_FLAG)  {
-		bus_claimed--;
-		if (bus_claimed <= 0) {
-			bus_claimed = 0;
-			ImcWakeup(NULL);
-		}
-	}
-#endif
+int chipset_volatile_group_begin(const struct spi_flash *flash)
+{
+	if (!IS_ENABLED (CONFIG_HUDSON_IMC_FWM))
+		return 0;
+
+	ImcSleep(NULL);
+	return 0;
+}
+
+int chipset_volatile_group_end(const struct spi_flash *flash)
+{
+	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 @@
 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 @@
 	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 @@
 
 	WaitForEcLDN9MailboxCmdAck();
 }
-#endif
 
 int spi_claim_bus(struct spi_slave *slave)
 {
-#if IS_ENABLED (CONFIG_SB800_IMC_FWM)
-
-	if (slave->rw == SPI_WRITE_FLAG) {
-		bus_claimed++;
-		if (bus_claimed == 1)
-			ImcSleep();
-	}
-#endif
-
+	/* Nothing is required. */
 	return 0;
 }
 
 void spi_release_bus(struct spi_slave *slave)
 {
-#if IS_ENABLED (CONFIG_SB800_IMC_FWM)
+	/* Nothing is required. */
+	return;
+}
 
-	if (slave->rw == SPI_WRITE_FLAG)  {
-		bus_claimed--;
-		if (bus_claimed <= 0) {
-			bus_claimed = 0;
-			ImcWakeup();
-		}
-	}
-#endif
+int chipset_volatile_group_begin(const struct spi_flash *flash)
+{
+	if (!IS_ENABLED(CONFIG_SB800_IMC_FWM))
+		return 0;
+
+	ImcSleep();
+	return 0;
+}
+
+int chipset_volatile_group_end(const struct spi_flash *flash)
+{
+	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_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 @@
 		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 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 @@
 		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 @@
 	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)
 	{