soc/intel/common: Add spinlock protection to fast SPI flash operations

Add spinlock synchronization to prevent concurrent SPI flash controller
access from multiple CPUs in SMP environments. The spinlock serializes
access to the SPI controller hardware in exec_sync_hwseq_xfer().

If SMP is not enabled, spinlock functions are no-ops, so this change
is safe for both SMP and non-SMP configurations.

This resolves an issue seen on the Starlabs Starfighter MTL where
multiple SPI transaction errors occurred when reading option variables
stored in SMMSTORE:

[ERROR] SPI Transaction Error at Flash Offset 103002a HSFSTS = 0x01016022
[ERROR] SPI Transaction Error at Flash Offset 1030004 HSFSTS = 0x01006022
[ERROR] SPI Transaction Error at Flash Offset 1030000 HSFSTS = 0x3f006022
...

Change-Id: Ic3003b0a986b587622102b6f36714bcb16c3d976
Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/90283
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Sean Rhodes <sean@starlabs.systems>
Reviewed-by: Jérémy Compostella <jeremy.compostella@intel.com>
This commit is contained in:
Matt DeVillier 2025-11-29 15:03:34 -06:00
commit 5034f8629f

View file

@ -9,6 +9,7 @@
#include <spi_flash.h>
#include <string.h>
#include <timer.h>
#include <smp/spinlock.h>
/* Helper to create a FAST_SPI context on API entry. */
#define BOILERPLATE_CREATE_CTX(ctx) \
@ -32,6 +33,9 @@ struct fast_spi_flash_ctx {
uintptr_t mmio_base;
};
/* Spinlock to protect concurrent SPI flash controller access from multiple CPUs */
DECLARE_SPIN_LOCK(fast_spi_lock);
static void _fast_spi_flash_get_ctx(struct fast_spi_flash_ctx *ctx)
{
ctx->mmio_base = (uintptr_t)fast_spi_get_bar();
@ -160,15 +164,28 @@ static int exec_sync_hwseq_xfer(struct fast_spi_flash_ctx *ctx,
uint32_t hsfsts_cycle, uint32_t flash_addr,
size_t len)
{
int ret;
/* Protect SPI flash controller from concurrent access by multiple CPUs.
* The spinlock serializes access to the SPI controller hardware.
* If SMP is not enabled, spinlock functions are no-ops.
*/
spin_lock(&fast_spi_lock);
if (wait_for_hwseq_spi_cycle_complete(ctx) != SUCCESS) {
printk(BIOS_ERR, "SPI Transaction Timeout (Exceeded %d ms) due to prior"
" operation at Flash Offset %x\n",
SPIBAR_HWSEQ_XFER_TIMEOUT_MS, flash_addr);
return E_TIMEOUT;
ret = E_TIMEOUT;
goto unlock;
}
start_hwseq_xfer(ctx, hsfsts_cycle, flash_addr, len);
return wait_for_hwseq_xfer(ctx, flash_addr);
ret = wait_for_hwseq_xfer(ctx, flash_addr);
unlock:
spin_unlock(&fast_spi_lock);
return ret;
}
int fast_spi_cycle_in_progress(void)