On Mon, Aug 31, 2026 at 12:37:21AM -0500, Mario Limonciello wrote:
> Some TPM implementations, particularly fTPM using the CRB interface,
> require the TPM to transition through idle and ready states for each
> command rather than once per session.
> 
> According to the TCG PC Client Platform TPM Profile (PTP) specification,
> the CRB interface includes an idle/ready handshake for power management.
> Software should set cmdReady to transition the TPM from idle to ready
> state before sending a command, and set goIdle when finished to allow
> the TPM to enter a low-power state.
> 
> The current implementation calls cmd_ready once during tpm_chip_start()
> and go_idle once during tpm_chip_stop(). During the startup sequence,
> multiple commands are sent between these calls (self-test, get
> capabilities, session init). For fTPM, when subsequent commands are sent
> without transitioning to idle after the previous command completes, those
> commands timeout as the TPM is waiting for the idle transition.
> 
> Fix this by calling cmd_ready before each command transmission and
> go_idle after receiving each response in tpm_try_transmit(). For TPM
> implementations that don't require per-command transitions, the callbacks
> can return immediately (as many already do based on the start method).
> 
> This resolves timeout errors during TPM initialization on systems where
> BIOS has already performed TPM startup, as the kernel can now properly
> communicate with the fTPM during auto-startup.
> 
> Signed-off-by: Mario Limonciello <[email protected]>
> ---
>  drivers/char/tpm/tpm-interface.c | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
> 
> diff --git a/drivers/char/tpm/tpm-interface.c 
> b/drivers/char/tpm/tpm-interface.c
> index f745a098908b3..a98ddca876152 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -113,6 +113,15 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, 
> void *buf, size_t bufsiz)
>               return -E2BIG;
>       }
>  
> +     if (chip->ops->cmd_ready) {
> +             rc = chip->ops->cmd_ready(chip);
> +             if (rc) {
> +                     dev_err(&chip->dev,
> +                             "%s: cmd_ready(): error %d\n", __func__, rc);
> +                     return rc;
> +             }
> +     }
> +
>       rc = chip->ops->send(chip, buf, bufsiz, count);
>       if (rc < 0) {
>               if (rc != -EPIPE)
> @@ -181,6 +190,12 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, 
> void *buf, size_t bufsiz)
>       if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length))
>               rc = -EFAULT;
>  
> +     if (chip->ops->go_idle) {
> +             int idle_rc = chip->ops->go_idle(chip);
> +             if (idle_rc && !rc)
> +                     rc = idle_rc;
> +     }
> +
>       return rc ? rc : len;
>  }
>  
> -- 
> 2.43.0
> 

I think this should remove also redundant calls to cmd_ready and
go_idle.

BR, Jarkko

Reply via email to