From ec34fa5e43bfbef5a93394db04ff3b03fdfe3f44 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Fri, 12 Apr 2013 11:04:36 +0000 Subject: [PATCH] tpm: Add support for new Infineon I2C TPM (SLB 9645 TT 1.2 I2C) Add support for Infineon's new SLB 9645 TT 1.2 I2C TPMs, which supports clockstretching, combined reads and a bus speed of up to 400khz. The device also has a new device id. This is based on the kernel patch provided by Infineon : https://gerrit.chromium.org/gerrit/42332 Signed-off-by: Vincent Palatin Signed-off-by: Simon Glass Reviewed-by: Luigi Semenzato Reviewed-by: Simon Glass Reviewed-by: Vincent Palatin Tested-by: Tom Wai-Hong Tam Tested-by: Vincent Palatin --- drivers/tpm/slb9635_i2c/tpm_tis_i2c.c | 109 +++++++++++++++++++++++++++------- drivers/tpm/tis_i2c.c | 4 ++ include/fdtdec.h | 1 + lib/fdtdec.c | 1 + 4 files changed, 92 insertions(+), 23 deletions(-) diff --git a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c b/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c index 82a41bf..c2e1041 100644 --- a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c +++ b/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c @@ -37,12 +37,15 @@ */ #include +#include #include #include #include "compatibility.h" #include "tpm.h" +DECLARE_GLOBAL_DATA_PTR; + /* max. buffer size supported by our tpm */ #ifdef TPM_BUFSIZE #undef TPM_BUFSIZE @@ -65,12 +68,26 @@ #define SLEEP_DURATION_LONG 210 /* in usec */ /* expected value for DIDVID register */ -#define TPM_TIS_I2C_DID_VID 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L +#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L + +enum i2c_chip_type { + SLB9635, + SLB9645, + UNKNOWN, +}; + +static const char * const chip_name[] = { + [SLB9635] = "slb9635tt", + [SLB9645] = "slb9645tt", + [UNKNOWN] = "unknown/fallback to slb9635", +}; /* Structure to store I2C TPM specific stuff */ struct tpm_inf_dev { uint addr; u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ + enum i2c_chip_type chip_type; }; static struct tpm_inf_dev tpm_dev = { @@ -98,27 +115,47 @@ int iic_tpm_read(u8 addr, u8 *buffer, size_t len) uint myaddr = addr; /* we have to use uint here, uchar hangs the board */ - for (count = 0; count < MAX_COUNT; count++) { - rc = i2c_write(tpm_dev.addr, 0, 0, (uchar *)&myaddr, 1); - if (rc == 0) - break; /*success, break to skip sleep*/ - - udelay(SLEEP_DURATION); - } + if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) { + /* slb9635 protocol should work in both cases */ + for (count = 0; count < MAX_COUNT; count++) { + rc = i2c_write(tpm_dev.addr, 0, 0, + (uchar *)&myaddr, 1); + if (rc == 0) + break; /* success, break to skip sleep */ - if (rc) - return -rc; + udelay(SLEEP_DURATION); + } - /* After the TPM has successfully received the register address it needs - * some time, thus we're sleeping here again, before retrieving the data - */ - for (count = 0; count < MAX_COUNT; count++) { - udelay(SLEEP_DURATION); - rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len); - if (rc == 0) - break; /*success, break to skip sleep*/ + if (rc) + return -rc; + + /* After the TPM has successfully received the register address + * it needs some time, thus we're sleeping here again, before + * retrieving the data + */ + for (count = 0; count < MAX_COUNT; count++) { + udelay(SLEEP_DURATION); + rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len); + if (rc == 0) + break; /* success, break to skip sleep */ + } + } else { + /* use a combined read for newer chips + * unfortunately the smbus functions are not suitable due to + * the 32 byte limit of the smbus. + * retries should usually not be needed, but are kept just to + * be safe on the safe side. + */ + for (count = 0; count < MAX_COUNT; count++) { + rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len); + if (rc == 0) + break; /* break here to skip sleep */ + udelay(SLEEP_DURATION); + } } + /* take care of 'guard time' */ + udelay(SLEEP_DURATION); if (rc) return -rc; @@ -139,11 +176,13 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, for (count = 0; count < max_count; count++) { rc = i2c_write(tpm_dev.addr, 0, 0, tpm_dev.buf, len + 1); if (rc == 0) - break; /*success, break to skip sleep*/ + break; /* success, break to skip sleep */ udelay(sleep_time); } + /* take care of 'guard time' */ + udelay(SLEEP_DURATION); if (rc) return -rc; @@ -490,12 +529,27 @@ static struct tpm_vendor_specific tpm_tis_i2c = { .req_canceled = TPM_STS_COMMAND_READY, }; +static enum i2c_chip_type tpm_vendor_chip_type(void) +{ +#ifdef CONFIG_OF_CONTROL + const void *blob = gd->fdt_blob; + + if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9645_TPM) >= 0) + return SLB9645; + + if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM) >= 0) + return SLB9635; +#endif + return UNKNOWN; +} + /* initialisation of i2c tpm */ int tpm_vendor_init(uint32_t dev_addr) { u32 vendor; + u32 expected_did_vid; uint old_addr; int rc = 0; struct tpm_chip *chip; @@ -504,6 +558,8 @@ int tpm_vendor_init(uint32_t dev_addr) if (dev_addr != 0) tpm_dev.addr = dev_addr; + tpm_dev.chip_type = tpm_vendor_chip_type(); + chip = tpm_register_hardware(&tpm_tis_i2c); if (chip < 0) { rc = -ENODEV; @@ -530,15 +586,22 @@ int tpm_vendor_init(uint32_t dev_addr) goto out_release; } - /* create DID_VID register value, after swapping to little-endian */ - vendor = be32_to_cpu(vendor); + if (tpm_dev.chip_type == SLB9635) { + vendor = be32_to_cpu(vendor); + expected_did_vid = TPM_TIS_I2C_DID_VID_9635; + } else { + /* device id and byte order has changed for newer i2c tpms */ + expected_did_vid = TPM_TIS_I2C_DID_VID_9645; + } - if (vendor != TPM_TIS_I2C_DID_VID) { + if (tpm_dev.chip_type != UNKNOWN && vendor != expected_did_vid) { + dev_err(dev, "vendor id did not match! ID was %08x\n", vendor); rc = -ENODEV; goto out_release; } - dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16); + dev_info(dev, "1.2 TPM (chip type %s device-id 0x%X)\n", + chip_name[tpm_dev.chip_type], vendor >> 16); /* * A timeout query to TPM can be placed here. diff --git a/drivers/tpm/tis_i2c.c b/drivers/tpm/tis_i2c.c index e818fba..22554e1 100644 --- a/drivers/tpm/tis_i2c.c +++ b/drivers/tpm/tis_i2c.c @@ -68,6 +68,10 @@ static int tpm_decode_config(struct tpm *dev) node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM); if (node < 0) { + node = fdtdec_next_compatible(blob, 0, + COMPAT_INFINEON_SLB9645_TPM); + } + if (node < 0) { debug("%s: Node not found\n", __func__); return -1; } diff --git a/include/fdtdec.h b/include/fdtdec.h index 4e8032b..1ece612 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -93,6 +93,7 @@ enum fdt_compat_id { COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */ COMPAT_MAXIM_98095_CODEC, /* MAX98095 Codec */ COMPAT_INFINEON_SLB9635_TPM, /* Infineon SLB9635 TPM */ + COMPAT_INFINEON_SLB9645_TPM, /* Infineon SLB9645 TPM */ COMPAT_COUNT, }; diff --git a/lib/fdtdec.c b/lib/fdtdec.c index ac1fe0b..005ad3d 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -66,6 +66,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(GENERIC_SPI_FLASH, "spi-flash"), COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"), COMPAT(INFINEON_SLB9635_TPM, "infineon,slb9635-tpm"), + COMPAT(INFINEON_SLB9645_TPM, "infineon,slb9645-tpm"), }; const char *fdtdec_get_compatible(enum fdt_compat_id id)