git: a229432d3ed0 - main - tpm_tis: Restore validated interrupts after resume
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 03 Sep 2026 03:56:10 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=a229432d3ed05282f7cf1510a06d6d93d554c1df
commit a229432d3ed05282f7cf1510a06d6d93d554c1df
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-26 10:54:59 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-03 03:55:24 +0000
tpm_tis: Restore validated interrupts after resume
TIS interrupt routing and enable registers may lose their state across
S3, while the driver retains its software indication that interrupts
work. A subsequent locality or command wait can then sleep for an
interrupt that cannot arrive.
Remember whether interrupts worked before suspend and restore the
vector, pending status, and enable mask before TPM2_Startup. Put the
transport in polling mode first; the interrupt handler promotes it back
to interrupt waits only after observing an interrupt from the restored
configuration. If register restoration fails, Startup and subsequent
commands continue using polling.
Preserve the initial interrupt-enable mask, including the firmware's
trigger and polarity selection proven by the attach time interrupt test,
and restore that exact mask rather than accepting post-S3 defaults.
Program the same safe baseline for polling devices during attach and
resume. Acquire locality, disable global interrupt delivery, and
acknowledge pending status so firmware cannot leave interrupts armed
without a handler.
Use the same register programming helper during attach and resume, and
stop trying to configure interrupts after a locality acquisition
failure.
Three consecutive device suspend and resume cycles completed on a Lenovo
TPM2 FIFO device without an IRQ resource. GetRandom succeeded after
each resume, and module detach completed without errors.
Reviewed by: kevans
MFC after: 2 weeks
Sponsored by: BBOX.io
Differential Revision: https://reviews.freebsd.org/D59197
---
sys/dev/tpm/tpm20.h | 1 +
sys/dev/tpm/tpm_tis_core.c | 104 ++++++++++++++++++++++++++++++++++-----------
2 files changed, 81 insertions(+), 24 deletions(-)
diff --git a/sys/dev/tpm/tpm20.h b/sys/dev/tpm/tpm20.h
index 7570812219b9..3bf59112bed8 100644
--- a/sys/dev/tpm/tpm20.h
+++ b/sys/dev/tpm/tpm20.h
@@ -127,6 +127,7 @@ struct tpm_sc {
void *intr_cookie;
int intr_type; /* Current event type */
+ uint32_t intr_mask; /* Saved TIS interrupt configuration */
bool interrupts;
bool common_initialized;
diff --git a/sys/dev/tpm/tpm_tis_core.c b/sys/dev/tpm/tpm_tis_core.c
index 15370e648f1e..f4b8ef23b3c7 100644
--- a/sys/dev/tpm/tpm_tis_core.c
+++ b/sys/dev/tpm/tpm_tis_core.c
@@ -79,7 +79,9 @@ static int tpmtis_detach(device_t dev);
static void tpmtis_intr_handler(void *arg);
+static bool tpmtis_program_intr(struct tpm_sc *sc, bool enable);
static void tpmtis_setup_intr(struct tpm_sc *sc);
+static int tpmtis_resume(device_t dev);
static bool tpmtis_read_bytes(struct tpm_sc *sc, size_t count, uint8_t *buf);
static bool tpmtis_write_bytes(struct tpm_sc *sc, size_t count, uint8_t *buf);
@@ -131,8 +133,7 @@ skip_irq:
tpmtis_detach(dev);
return (result);
}
- if (sc->intr_cookie != NULL)
- tpmtis_setup_intr(sc);
+ tpmtis_setup_intr(sc);
return (0);
}
@@ -182,43 +183,98 @@ tpmtis_test_intr(struct tpm_sc *sc)
sx_xunlock(&sc->dev_lock);
}
-static void
-tpmtis_setup_intr(struct tpm_sc *sc)
+static bool
+tpmtis_program_intr(struct tpm_sc *sc, bool enable)
{
+ rman_res_t irq;
uint32_t reg;
- uint8_t irq;
- irq = bus_get_resource_start(sc->dev, SYS_RES_IRQ, sc->irq_rid);
+ sx_assert(&sc->dev_lock, SA_XLOCKED);
- /*
- * SIRQ has to be between 1 - 15.
- * I found a system with ACPI table that reported a value of 0x2d.
- * An attempt to use such value resulted in an interrupt storm.
- */
- if (irq == 0 || irq > 0xF)
- return;
+ if (enable) {
+ irq = bus_get_resource_start(sc->dev, SYS_RES_IRQ,
+ sc->irq_rid);
+
+ /*
+ * SIRQ has to be between 1 - 15. A system reporting 0x2d
+ * produced an interrupt storm when that value was used.
+ */
+ if (irq == 0 || irq > 0xF)
+ return (false);
+ }
- if(!tpmtis_request_locality(sc, 0))
- sc->interrupts = false;
+ if (!tpmtis_request_locality(sc, 0))
+ return (false);
- TPM_WRITE_1(sc->dev, TPM_INT_VECTOR, irq);
+ /* Disable delivery before acknowledging or reconfiguring interrupts. */
+ reg = TPM_READ_4(sc->dev, TPM_INT_ENABLE);
+ reg &= ~TPM_INT_ENABLE_GLOBAL_ENABLE;
+ TPM_WRITE_4(sc->dev, TPM_INT_ENABLE, reg);
/* Clear all pending interrupts. */
reg = TPM_READ_4(sc->dev, TPM_INT_STS);
TPM_WRITE_4(sc->dev, TPM_INT_STS, reg);
- reg = TPM_READ_4(sc->dev, TPM_INT_ENABLE);
- reg |= TPM_INT_ENABLE_GLOBAL_ENABLE |
- TPM_INT_ENABLE_DATA_AVAIL |
- TPM_INT_ENABLE_LOC_CHANGE |
- TPM_INT_ENABLE_CMD_RDY |
- TPM_INT_ENABLE_STS_VALID;
- TPM_WRITE_4(sc->dev, TPM_INT_ENABLE, reg);
+ if (enable) {
+ TPM_WRITE_1(sc->dev, TPM_INT_VECTOR, (uint8_t)irq);
+
+ if (sc->intr_mask == 0) {
+ reg = TPM_READ_4(sc->dev, TPM_INT_ENABLE);
+ reg |= TPM_INT_ENABLE_DATA_AVAIL |
+ TPM_INT_ENABLE_LOC_CHANGE |
+ TPM_INT_ENABLE_CMD_RDY |
+ TPM_INT_ENABLE_STS_VALID;
+ reg &= ~TPM_INT_ENABLE_GLOBAL_ENABLE;
+ sc->intr_mask = reg;
+ }
+ TPM_WRITE_4(sc->dev, TPM_INT_ENABLE,
+ sc->intr_mask | TPM_INT_ENABLE_GLOBAL_ENABLE);
+ }
tpmtis_relinquish_locality(sc);
+ return (true);
+}
+
+static void
+tpmtis_setup_intr(struct tpm_sc *sc)
+{
+ bool configured, enable;
+
+ sc->interrupts = false;
+ enable = sc->intr_cookie != NULL;
+ sx_xlock(&sc->dev_lock);
+ configured = tpmtis_program_intr(sc, enable);
+ sx_xunlock(&sc->dev_lock);
+ if (!configured || !enable)
+ return;
tpmtis_test_intr(sc);
}
+static int
+tpmtis_resume(device_t dev)
+{
+ struct tpm_sc *sc;
+ bool restore_intr;
+
+ sc = device_get_softc(dev);
+ sx_xlock(&sc->dev_lock);
+ restore_intr = sc->interrupts;
+
+ /*
+ * Interrupt routing and enable state may be lost across suspend. Keep
+ * the transport in polling mode until a restored interrupt is actually
+ * observed by the handler.
+ */
+ sc->interrupts = false;
+ if (!tpmtis_program_intr(sc, restore_intr))
+ device_printf(dev,
+ "failed to %s interrupts; using polling\n",
+ restore_intr ? "restore" : "disable");
+ sx_xunlock(&sc->dev_lock);
+
+ return (tpm20_resume(dev));
+}
+
static void
tpmtis_intr_handler(void *arg)
{
@@ -501,7 +557,7 @@ static device_method_t tpmtis_methods[] = {
DEVMETHOD(device_detach, tpmtis_detach),
DEVMETHOD(device_shutdown, tpm20_shutdown),
DEVMETHOD(device_suspend, tpm20_suspend),
- DEVMETHOD(device_resume, tpm20_resume),
+ DEVMETHOD(device_resume, tpmtis_resume),
DEVMETHOD(tpm_transmit, tpmtis_transmit),
DEVMETHOD_END
};