// 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), };