git: 55c97a16dfcc - stable/15 - tpm_tis: Restore validated interrupts after resume
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 11 Oct 2026 01:06:59 UTC
The branch stable/15 has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=55c97a16dfcc1237ba8fdd37175987ca3281b29c
commit 55c97a16dfcc1237ba8fdd37175987ca3281b29c
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-26 10:54:59 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-11 01:06:47 +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
Sponsored by: BBOX.io
Differential Revision: https://reviews.freebsd.org/D59197
(cherry picked from commit a229432d3ed05282f7cf1510a06d6d93d554c1df)
---
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 762aa1f7fd75..66263a13ce97 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)
{
@@ -491,7 +547,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
};