diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index eceff27d5f8862d5a3f9de87d495e107a17a15e9..d59102d9a6bdcd78e840a176b00d0565574f88a8 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -185,6 +185,15 @@ config TPM2_TIS_SPI to the device using the standard TPM Interface Specification (TIS) protocol. +config TPM2_TIS_I2C + bool "Enable support for TPMv2.x I2C chips" + depends on TPM_V2 && DM_I2C + help + This driver supports TPMv2.x devices connected on the I2C bus. + The usual TPM operations and the 'tpm' command can be used to talk + to the device using the standard TPM Interface Specification (TIS) + protocol. + config TPM2_FTPM_TEE bool "TEE based fTPM Interface" depends on TEE && OPTEE && TPM_V2 diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile index 51725230c780f7101cd408efb5289964eb5d83d5..9540fd7fe78c93e5bbb56685751c99ddf57d3fb8 100644 --- a/drivers/tpm/Makefile +++ b/drivers/tpm/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o obj-$(CONFIG_$(SPL_TPL_)TPM2_CR50_I2C) += cr50_i2c.o obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o sandbox_common.o obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_core.o tpm2_tis_spi.o +obj-$(CONFIG_TPM2_TIS_I2C) += tpm2_tis_core.o tpm2_tis_i2c.o obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o obj-$(CONFIG_TPM2_MMIO) += tpm2_tis_core.o tpm2_tis_mmio.o diff --git a/drivers/tpm/tpm2_tis_core.c b/drivers/tpm/tpm2_tis_core.c index 51392c4584986c83bdb5ebd39f8afed0a0e75506..985a816219867921d7db971c3bb004434c9b4d22 100644 --- a/drivers/tpm/tpm2_tis_core.c +++ b/drivers/tpm/tpm2_tis_core.c @@ -433,15 +433,16 @@ int tpm_tis_init(struct udevice *dev) log_err("Driver bug. No bus ops defined\n"); return -1; } - ret = tpm_tis_request_locality(dev, 0); - if (ret) - return ret; chip->timeout_a = TIS_SHORT_TIMEOUT_MS; chip->timeout_b = TIS_LONG_TIMEOUT_MS; chip->timeout_c = TIS_SHORT_TIMEOUT_MS; chip->timeout_d = TIS_SHORT_TIMEOUT_MS; + ret = tpm_tis_request_locality(dev, 0); + if (ret) + return ret; + /* Disable interrupts */ phy_ops->read32(dev, TPM_INT_ENABLE(chip->locality), &tmp); tmp |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | diff --git a/drivers/tpm/tpm2_tis_i2c.c b/drivers/tpm/tpm2_tis_i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..99d1cf218dafc2fdc8e97b2841a3dbe6e7bf0510 --- /dev/null +++ b/drivers/tpm/tpm2_tis_i2c.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2022 IBM Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm_tis.h" +#include "tpm_internal.h" + +struct tpm_tis_chip_data { + unsigned int pcr_count; + unsigned int pcr_select_min; +}; + +static uint tpm_tis_i2c_address_to_register(u32 addr) +{ + addr &= 0xFFF; + + /* + * Adapt register addresses that have changed compared to older TIS + * version. + */ + switch (addr) { + case TPM_ACCESS(0): + return 0x04; + case TPM_DID_VID(0): + return 0x48; + case TPM_RID(0): + return 0x4C; + default: + return addr; + } +} + +static int tpm_tis_i2c_read(struct udevice *dev, u32 addr, u16 len, u8 *in) +{ + int rc; + int count = 0; + uint reg = tpm_tis_i2c_address_to_register(addr); + + do { + rc = dm_i2c_read(dev, reg, in, len); + udelay(SLEEP_DURATION_US); + } while (rc && count++ < MAX_COUNT); + + return rc; +} + +static int tpm_tis_i2c_write(struct udevice *dev, u32 addr, u16 len, + const u8 *out) +{ + int rc; + int count = 0; + uint reg = tpm_tis_i2c_address_to_register(addr); + + do { + rc = dm_i2c_write(dev, reg, out, len); + udelay(SLEEP_DURATION_US); + } while (rc && count++ < MAX_COUNT); + + return rc; +} + +static int tpm_tis_i2c_read32(struct udevice *dev, u32 addr, u32 *result) +{ + __le32 result_le; + int rc; + + rc = tpm_tis_i2c_read(dev, addr, sizeof(u32), (u8 *)&result_le); + if (!rc) + *result = le32_to_cpu(result_le); + + return rc; +} + +static int tpm_tis_i2c_write32(struct udevice *dev, u32 addr, u32 value) +{ + __le32 value_le = cpu_to_le32(value); + + return tpm_tis_i2c_write(dev, addr, sizeof(value), (u8 *)&value_le); +} + +static struct tpm_tis_phy_ops phy_ops = { + .read_bytes = tpm_tis_i2c_read, + .write_bytes = tpm_tis_i2c_write, + .read32 = tpm_tis_i2c_read32, + .write32 = tpm_tis_i2c_write32, +}; + +static int tpm_tis_i2c_probe(struct udevice *udev) +{ + struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(udev); + struct tpm_chip_priv *priv = dev_get_uclass_priv(udev); + int rc; + u8 loc = 0; + + tpm_tis_ops_register(udev, &phy_ops); + + /* + * Force locality 0. The core driver doesn't actually write the + * locality register and instead just reads/writes various access + * bits of the selected locality. + */ + rc = dm_i2c_write(udev, 0, &loc, 1); + if (rc) + return rc; + + rc = tpm_tis_init(udev); + if (rc) + return rc; + + priv->pcr_count = drv_data->pcr_count; + priv->pcr_select_min = drv_data->pcr_select_min; + priv->version = TPM_V2; + + return 0; +} + +static int tpm_tis_i2c_remove(struct udevice *udev) +{ + return tpm_tis_cleanup(udev); +} + +static const struct tpm_ops tpm_tis_i2c_ops = { + .open = tpm_tis_open, + .close = tpm_tis_close, + .get_desc = tpm_tis_get_desc, + .send = tpm_tis_send, + .recv = tpm_tis_recv, + .cleanup = tpm_tis_cleanup, +}; + +static const struct tpm_tis_chip_data tpm_tis_std_chip_data = { + .pcr_count = 24, + .pcr_select_min = 3, +}; + +static const struct udevice_id tpm_tis_i2c_ids[] = { + { + .compatible = "nuvoton,npct75x", + .data = (ulong)&tpm_tis_std_chip_data, + }, + { + .compatible = "tcg,tpm-tis-i2c", + .data = (ulong)&tpm_tis_std_chip_data, + }, + { } +}; + +U_BOOT_DRIVER(tpm_tis_i2c) = { + .name = "tpm_tis_i2c", + .id = UCLASS_TPM, + .of_match = tpm_tis_i2c_ids, + .ops = &tpm_tis_i2c_ops, + .probe = tpm_tis_i2c_probe, + .remove = tpm_tis_i2c_remove, + .priv_auto = sizeof(struct tpm_chip), +};