diff options
Diffstat (limited to 'src/drivers')
-rw-r--r-- | src/drivers/i2c/tpm/Kconfig | 5 | ||||
-rw-r--r-- | src/drivers/i2c/tpm/cr50.c | 62 | ||||
-rw-r--r-- | src/drivers/i2c/tpm/tpm.h | 1 |
3 files changed, 60 insertions, 8 deletions
diff --git a/src/drivers/i2c/tpm/Kconfig b/src/drivers/i2c/tpm/Kconfig index 1725eaf0ff..a5ab0771c4 100644 --- a/src/drivers/i2c/tpm/Kconfig +++ b/src/drivers/i2c/tpm/Kconfig @@ -30,6 +30,11 @@ config DRIVER_TPM_I2C_ADDR default 2 # FIXME, workaround for Kconfig BS depends on I2C_TPM +config DRIVER_TPM_I2C_IRQ + int "IRQ or GPE to use for TPM interrupt" + default -1 + depends on I2C_TPM + config DRIVER_I2C_TPM_ACPI bool "Generate I2C TPM ACPI device" default y if ARCH_X86 && I2C_TPM diff --git a/src/drivers/i2c/tpm/cr50.c b/src/drivers/i2c/tpm/cr50.c index 37f0881980..af7f2aef44 100644 --- a/src/drivers/i2c/tpm/cr50.c +++ b/src/drivers/i2c/tpm/cr50.c @@ -41,6 +41,9 @@ #include <timer.h> #include "tpm.h" +#if IS_ENABLED(CONFIG_ARCH_X86) +#include <arch/acpi.h> +#endif #define CR50_MAX_BUFSIZE 63 #define CR50_TIMEOUT_LONG_MS 2000 /* Long timeout while waiting for TPM */ @@ -55,6 +58,33 @@ struct tpm_inf_dev { static struct tpm_inf_dev g_tpm_dev CAR_GLOBAL; +/* Wait for interrupt to indicate the TPM is ready */ +static int cr50_i2c_wait_tpm_ready(struct tpm_chip *chip) +{ + struct stopwatch sw; + + if (!chip->vendor.irq_status) { + /* Fixed delay if interrupt not supported */ + mdelay(CR50_TIMEOUT_SHORT_MS); + return 0; + } + + stopwatch_init_msecs_expire(&sw, 5 * CR50_TIMEOUT_SHORT_MS); + + while (!chip->vendor.irq_status(chip->vendor.irq)) + if (stopwatch_expired(&sw)) + return -1; + + return 0; +} + +/* Clear pending interrupts */ +static void cr50_i2c_clear_tpm_irq(struct tpm_chip *chip) +{ + if (chip->vendor.irq_status) + chip->vendor.irq_status(chip->vendor.irq); +} + /* * cr50_i2c_read() - read from TPM register * @@ -77,6 +107,9 @@ static int cr50_i2c_read(struct tpm_chip *chip, uint8_t addr, if (tpm_dev->addr == 0) return -1; + /* Clear interrupt before starting transaction */ + cr50_i2c_clear_tpm_irq(chip); + /* Send the register address byte to the TPM */ if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, &addr, 1)) { printk(BIOS_ERR, "%s: Address write failed\n", __func__); @@ -84,7 +117,8 @@ static int cr50_i2c_read(struct tpm_chip *chip, uint8_t addr, } /* Wait for TPM to be ready with response data */ - mdelay(CR50_TIMEOUT_SHORT_MS); + if (cr50_i2c_wait_tpm_ready(chip) < 0) + return -1; /* Read response data from the TPM */ if (i2c_read_raw(tpm_dev->bus, tpm_dev->addr, buffer, len)) { @@ -123,6 +157,9 @@ static int cr50_i2c_write(struct tpm_chip *chip, tpm_dev->buf[0] = addr; memcpy(tpm_dev->buf + 1, buffer, len); + /* Clear interrupt before starting transaction */ + cr50_i2c_clear_tpm_irq(chip); + /* Send write request buffer with address */ if (i2c_write_raw(tpm_dev->bus, tpm_dev->addr, tpm_dev->buf, len + 1)) { printk(BIOS_ERR, "%s: Error writing to TPM\n", __func__); @@ -130,9 +167,7 @@ static int cr50_i2c_write(struct tpm_chip *chip, } /* Wait for TPM to be ready */ - mdelay(CR50_TIMEOUT_SHORT_MS); - - return 0; + return cr50_i2c_wait_tpm_ready(chip); } static int check_locality(struct tpm_chip *chip, int loc) @@ -380,6 +415,7 @@ static void cr50_vendor_init(struct tpm_chip *chip) chip->vendor.recv = &cr50_i2c_tis_recv; chip->vendor.send = &cr50_i2c_tis_send; chip->vendor.cancel = &cr50_i2c_tis_ready; + chip->vendor.irq = CONFIG_DRIVER_TPM_I2C_IRQ; } int tpm_vendor_probe(unsigned bus, uint32_t addr) @@ -434,8 +470,18 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) cr50_vendor_init(chip); - /* Disable interrupts (not supported) */ - chip->vendor.irq = 0; + /* + * Interrupts are not supported this early in firmware, + * use use an arch-specific method to query for interrupt status. + */ + if (chip->vendor.irq > 0) { +#if IS_ENABLED(CONFIG_ARCH_X86) + /* Query GPE status for interrupt */ + chip->vendor.irq_status = &acpi_get_gpe; +#else + chip->vendor.irq = -1; +#endif + } if (request_locality(chip, 0) != 0) return -1; @@ -449,8 +495,8 @@ int tpm_vendor_init(struct tpm_chip *chip, unsigned bus, uint32_t dev_addr) goto out_err; } - printk(BIOS_DEBUG, "cr50 TPM %u:%02x (device-id 0x%X)\n", - tpm_dev->bus, tpm_dev->addr, vendor >> 16); + printk(BIOS_DEBUG, "cr50 TPM 2.0 (i2c %u:0x%02x irq %d id 0x%x)\n", + bus, dev_addr, chip->vendor.irq, vendor >> 16); chip->is_open = 1; return 0; diff --git a/src/drivers/i2c/tpm/tpm.h b/src/drivers/i2c/tpm/tpm.h index 048c848de2..b6dda1af47 100644 --- a/src/drivers/i2c/tpm/tpm.h +++ b/src/drivers/i2c/tpm/tpm.h @@ -74,6 +74,7 @@ struct tpm_vendor_specific { uint8_t req_complete_val; uint8_t req_canceled; int irq; + int (*irq_status)(int irq); int (*recv)(struct tpm_chip *, uint8_t *, size_t); int (*send)(struct tpm_chip *, uint8_t *, size_t); void (*cancel)(struct tpm_chip *); |