summaryrefslogtreecommitdiffstats
path: root/drivers/tpm
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2013-04-12 11:04:36 +0000
committerSimon Glass <sjg@chromium.org>2013-06-03 01:26:25 -0700
commitec34fa5e43bfbef5a93394db04ff3b03fdfe3f44 (patch)
tree1f5b45952a9440d6b4571313f0a4dcf98e94148b /drivers/tpm
parent5bdf46b71b26263d090ccdb1c6679afd085f0323 (diff)
downloadblackbird-obmc-uboot-ec34fa5e43bfbef5a93394db04ff3b03fdfe3f44.tar.gz
blackbird-obmc-uboot-ec34fa5e43bfbef5a93394db04ff3b03fdfe3f44.zip
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 <vpalatin@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Luigi Semenzato <semenzato@chromium.org> Reviewed-by: Simon Glass <sjg@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Tested-by: Tom Wai-Hong Tam <waihong@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'drivers/tpm')
-rw-r--r--drivers/tpm/slb9635_i2c/tpm_tis_i2c.c109
-rw-r--r--drivers/tpm/tis_i2c.c4
2 files changed, 90 insertions, 23 deletions
diff --git a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c b/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c
index 82a41bf5b2..c2e104189a 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*/
-
- 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 e818fbaf54..22554e1456 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;
}
OpenPOWER on IntegriCloud