summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/drivers/i2c/tpm/Kconfig12
-rw-r--r--src/drivers/i2c/tpm/cr50.c57
-rw-r--r--src/drivers/i2c/tpm/tpm.h1
3 files changed, 61 insertions, 9 deletions
diff --git a/src/drivers/i2c/tpm/Kconfig b/src/drivers/i2c/tpm/Kconfig
index db77b956b9..a5ab0771c4 100644
--- a/src/drivers/i2c/tpm/Kconfig
+++ b/src/drivers/i2c/tpm/Kconfig
@@ -2,9 +2,14 @@ config I2C_TPM
bool "I2C TPM"
depends on TPM || TPM2
+config MAINBOARD_HAS_I2C_TPM_CR50
+ bool
+ default n
+
choice
prompt "I2C TPM Driver"
- default I2C_TPM_GENERIC
+ default I2C_TPM_CR50 if MAINBOARD_HAS_I2C_TPM_CR50
+ default I2C_TPM_GENERIC if !MAINBOARD_HAS_I2C_TPM_CR50
depends on I2C_TPM
config I2C_TPM_GENERIC
@@ -25,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..d3c0efa2ac 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,26 @@ 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, 10 * CR50_TIMEOUT_SHORT_MS);
+
+ while (!chip->vendor.irq_status(chip->vendor.irq))
+ if (stopwatch_expired(&sw))
+ return -1;
+
+ return 0;
+}
+
/*
* cr50_i2c_read() - read from TPM register
*
@@ -77,6 +100,10 @@ static int cr50_i2c_read(struct tpm_chip *chip, uint8_t addr,
if (tpm_dev->addr == 0)
return -1;
+ /* Clear interrupt before starting transaction */
+ if (chip->vendor.irq_status)
+ chip->vendor.irq_status(chip->vendor.irq);
+
/* 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 +111,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 +151,10 @@ 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 */
+ if (chip->vendor.irq_status)
+ chip->vendor.irq_status(chip->vendor.irq);
+
/* 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 +162,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 +410,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 +465,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;
+ }
+ }
if (request_locality(chip, 0) != 0)
return -1;
@@ -449,8 +490,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 *);