Attention is currently required from: Christian Walter. Tim Wawrzynczak has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/61721 )
Change subject: drivers/i2c/tpm/cr50: Add support to get and set BOARD_CFG register ......................................................................
drivers/i2c/tpm/cr50: Add support to get and set BOARD_CFG register
The cr50 BOARD_CFG register has a bit to support extended interrupt pulses to the PCH. There is support for this in the SPI driver, but not in the I2C driver, and it may be required for some boards, therefore port the logic over to the I2C driver.
Signed-off-by: Tim Wawrzynczak twawrzynczak@chromium.org Change-Id: I9fcfed9fa133db2560c4a855376d249e865c1335 --- M src/drivers/i2c/tpm/cr50.c M src/drivers/i2c/tpm/tpm.h 2 files changed, 77 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/21/61721/1
diff --git a/src/drivers/i2c/tpm/cr50.c b/src/drivers/i2c/tpm/cr50.c index ac07f4d..d14d344 100644 --- a/src/drivers/i2c/tpm/cr50.c +++ b/src/drivers/i2c/tpm/cr50.c @@ -36,6 +36,12 @@ #define CR50_TIMEOUT_IRQ_MS 100 /* Timeout for TPM ready with IRQ */ #define CR50_DID_VID 0x00281ae0L
+#define CR50_BOARD_CFG_LOCKBIT_MASK 0x80000000U +#define CR50_BOARD_CFG_FEATUREBITS_MASK 0x3FFFFFFFU + +#define CR50_BOARD_CFG_100US_READY_PULSE 0x00000001U +#define CR50_BOARD_CFG_VALUE CR50_BOARD_CFG_100US_READY_PULSE + struct tpm_inf_dev { int bus; unsigned int addr; @@ -524,11 +530,71 @@ return 0; }
+static int cr50_fw_supports_board_cfg(struct cr50_firmware_version *version) +{ + /* Cr50 supports the CR50_BOARD_CFG register from version 0.5.5 / 0.6.5 + * and onwards. */ + if (version->epoch > 0 || version->major >= 7 + || (version->major >= 5 && version->minor >= 5)) + return 1; + + printk(BIOS_INFO, "Cr50 firmware does not support CR50_BOARD_CFG, version: %d.%d.%d\n", + version->epoch, version->major, version->minor); + + return 0; +} + static int first_access_this_boot(void) { return ENV_SEPARATE_VERSTAGE || ENV_BOOTBLOCK || !CONFIG(VBOOT); }
+static uint32_t cr50_get_board_cfg(void) +{ + uint32_t board_cfg_value; + + if (!cr50_fw_supports_board_cfg(&cr50_firmware_version)) + return 0; + + if (cr50_i2c_read(CR50_BOARD_CFG(0), (uint8_t *)&board_cfg_value, + sizeof(board_cfg_value))) { + printk(BIOS_INFO, "%s: Error reading from cr50\n", __func__); + return 0; + } + + return board_cfg_value & CR50_BOARD_CFG_FEATUREBITS_MASK; +} + +/* Set the BOARD_CFG register on the TPM chip to a particular compile-time constant value. */ +static void cr50_set_board_cfg(void) +{ + uint32_t board_cfg_value = cr50_get_board_cfg(); + + if (board_cfg_value == CR50_BOARD_CFG_VALUE) { + printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, matches desired = 0x%08x\n", + board_cfg_value, CR50_BOARD_CFG_VALUE); + return; + } + + if (board_cfg_value & CR50_BOARD_CFG_LOCKBIT_MASK) { + /* The high bit is set, meaning that the Cr50 is already locked on a particular + * value for the register, but not the one we wanted. */ + printk(BIOS_ERR, + "ERROR: Current CR50_BOARD_CFG = 0x%08x, does not match desired = 0x%08x\n", + board_cfg_value, CR50_BOARD_CFG_VALUE); + return; + } + + printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, setting to 0x%08x\n", + board_cfg_value, CR50_BOARD_CFG_VALUE); + + board_cfg_value = CR50_BOARD_CFG_VALUE; + if (cr50_i2c_write(CR50_BOARD_CFG(0), (uint8_t *)&board_cfg_value, sizeof(board_cfg_value))) + printk(BIOS_ERR, "%s: Error writing to cr50\n", __func__); + + printk(BIOS_ERR, "Reading back BOARD_CFG: %x\n", cr50_get_board_cfg()); +} + int tpm_vendor_init(struct tpm_chip *chip, unsigned int bus, uint32_t dev_addr) { uint32_t did_vid = 0; @@ -561,6 +627,9 @@ cr50_firmware_version.epoch, cr50_firmware_version.major, cr50_firmware_version.minor);
+ if (CONFIG(CR50_USE_LONG_INTERRUPT_PULSES) && first_access_this_boot()) + cr50_set_board_cfg(); + chip->is_open = 1; return 0; } @@ -568,6 +637,12 @@ void tpm_vendor_cleanup(struct tpm_chip *chip) { } + +bool cr50_is_long_interrupt_pulse_enabled(void) +{ + return cr50_get_board_cfg() & CR50_BOARD_CFG_100US_READY_PULSE; +} + void cr50_get_firmware_version(struct cr50_firmware_version *version) { *version = cr50_firmware_version; diff --git a/src/drivers/i2c/tpm/tpm.h b/src/drivers/i2c/tpm/tpm.h index b5b084a..cebec81 100644 --- a/src/drivers/i2c/tpm/tpm.h +++ b/src/drivers/i2c/tpm/tpm.h @@ -37,6 +37,7 @@ #define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) #define TPM_DID_VID(l) (0x0006 | ((l) << 4)) #define TPM_FW_VER(l) (0x000f | ((l) << 4)) +#define CR50_BOARD_CFG(l) (0x001c | ((l) << 4))
struct tpm_chip;
@@ -65,6 +66,7 @@
/* Get the cr50 firmware version information. */ void cr50_get_firmware_version(struct cr50_firmware_version *version); +bool cr50_is_long_interrupt_pulse_enabled(void);
/* ---------- Interface for TPM vendor ------------ */