aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/intel/common/block/fast_spi/fast_spi.c14
-rw-r--r--src/soc/intel/common/block/fast_spi/fast_spi_def.h1
-rw-r--r--src/soc/intel/common/block/include/intelblocks/fast_spi.h4
-rw-r--r--src/soc/intel/common/block/smm/smihandler.c12
4 files changed, 31 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/fast_spi/fast_spi.c b/src/soc/intel/common/block/fast_spi/fast_spi.c
index 58e3ca2c62..a3481c6fb5 100644
--- a/src/soc/intel/common/block/fast_spi/fast_spi.c
+++ b/src/soc/intel/common/block/fast_spi/fast_spi.c
@@ -387,6 +387,20 @@ void fast_spi_early_init(uintptr_t spi_base_address)
fast_spi_init();
}
+/* Clear SPI Synchronous SMI status bit and return its value. */
+bool fast_spi_clear_sync_smi_status(void)
+{
+ const uint32_t bios_cntl = pci_read_config32(PCH_DEV_SPI, SPI_BIOS_CONTROL);
+ const bool smi_asserted = bios_cntl & SPI_BIOS_CONTROL_SYNC_SS;
+ /*
+ * Do not unconditionally write 1 to clear SYNC_SS. Hardware could set
+ * SYNC_SS here (after we read but before we write SPI_BIOS_CONTROL),
+ * and the event would be lost when unconditionally clearing SYNC_SS.
+ */
+ pci_write_config32(PCH_DEV_SPI, SPI_BIOS_CONTROL, bios_cntl);
+ return smi_asserted;
+}
+
/* Read SPI Write Protect disable status. */
bool fast_spi_wpd_status(void)
{
diff --git a/src/soc/intel/common/block/fast_spi/fast_spi_def.h b/src/soc/intel/common/block/fast_spi/fast_spi_def.h
index 945feb072d..4f79b75da7 100644
--- a/src/soc/intel/common/block/fast_spi/fast_spi_def.h
+++ b/src/soc/intel/common/block/fast_spi/fast_spi_def.h
@@ -17,6 +17,7 @@
#define SPI_BIOS_CONTROL_PREFETCH_ENABLE (1 << 3)
#define SPI_BIOS_CONTROL_EISS (1 << 5)
#define SPI_BIOS_CONTROL_BILD (1 << 7)
+#define SPI_BIOS_CONTROL_SYNC_SS (1 << 8)
#define SPI_BIOS_CONTROL_EXT_BIOS_ENABLE (1 << 27)
#define SPI_BIOS_CONTROL_EXT_BIOS_LOCK_ENABLE (1 << 28)
#define SPI_BIOS_CONTROL_EXT_BIOS_LIMIT(x) ((x) & ~(0xfff))
diff --git a/src/soc/intel/common/block/include/intelblocks/fast_spi.h b/src/soc/intel/common/block/include/intelblocks/fast_spi.h
index 3ba240baf3..d0442566b3 100644
--- a/src/soc/intel/common/block/include/intelblocks/fast_spi.h
+++ b/src/soc/intel/common/block/include/intelblocks/fast_spi.h
@@ -65,6 +65,10 @@ void fast_spi_early_init(uintptr_t spi_base_address);
*/
extern const struct spi_ctrlr fast_spi_flash_ctrlr;
/*
+ * Clear SPI Synchronous SMI status bit and return its value.
+ */
+bool fast_spi_clear_sync_smi_status(void);
+/*
* Read SPI Write protect disable bit.
*/
bool fast_spi_wpd_status(void);
diff --git a/src/soc/intel/common/block/smm/smihandler.c b/src/soc/intel/common/block/smm/smihandler.c
index a15bc7c8c5..2c3364ab06 100644
--- a/src/soc/intel/common/block/smm/smihandler.c
+++ b/src/soc/intel/common/block/smm/smihandler.c
@@ -384,6 +384,18 @@ void smihandler_southbridge_tco(
{
uint32_t tco_sts = pmc_clear_tco_status();
+ /*
+ * SPI synchronous SMIs are TCO SMIs, but they do not have a status
+ * bit in the TCO_STS register. Furthermore, the TCO_STS bit in the
+ * SMI_STS register is continually set until the SMI handler clears
+ * the SPI synchronous SMI status bit in the SPI controller. To not
+ * risk missing any other TCO SMIs, do not clear the TCO_STS bit in
+ * this SMI handler invocation. If the TCO_STS bit remains set when
+ * returning from SMM, another SMI immediately happens which clears
+ * the TCO_STS bit and handles any pending events.
+ */
+ fast_spi_clear_sync_smi_status();
+
/* Any TCO event? */
if (!tco_sts)
return;