diff options
| -rw-r--r-- | drivers/tpm/slb9635_i2c/tpm_tis_i2c.c | 107 | ||||
| -rw-r--r-- | drivers/tpm/tis_i2c.c | 4 | ||||
| -rw-r--r-- | include/fdtdec.h | 1 | ||||
| -rw-r--r-- | lib/fdtdec.c | 1 | 
4 files changed, 91 insertions, 22 deletions
| diff --git a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c b/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c index 82a41bf5b..c2e104189 100644 --- a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c +++ b/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c @@ -37,12 +37,15 @@   */  #include <common.h> +#include <fdtdec.h>  #include <i2c.h>  #include <linux/types.h>  #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*/ +	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 */ -		udelay(SLEEP_DURATION); -	} +			udelay(SLEEP_DURATION); +		} -	if (rc) -		return -rc; +		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*/ +		/* 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 e818fbaf5..22554e145 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 4e8032ba6..1ece6122f 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 ac1fe0be2..005ad3d53 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) |