summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@chromium.org>2012-03-19 13:12:13 -0700
committerGerrit <chrome-bot@google.com>2012-04-10 10:55:20 -0700
commit15350ddaccdf14e7e274463db97b72c47a73b10b (patch)
tree74d508636d709c6b094254e307aa13fe066eb69b /drivers
parent2f2f858faddd3cce54f7c64bc8fc8b596c1ddfaf (diff)
x86: Fix TPM driver to work with multiple vendor TPMs
- Fix bug in traversal of vendor name list. - Sending "command ready" needs additional logic to handle TPMs that need that bit set twice: once to empty the read FIFOs and once to actualy set command ready. - Certain TPMs need a small delay between requesting locality and attempting to set command ready or they will hang the bus. BUG=chrome-os-partner:8558 TEST=manual Successful boot and suspend/resume with all TPMs listed in the driver vendor/device list. Change-Id: I22021b24f9498c3cafe0e1d5f1c6562ea0be5aad Signed-off-by: Duncan Laurie <dlaurie@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/18480 Reviewed-by: Ronald G. Minnich <rminnich@chromium.org> Reviewed-by: Stefan Reinauer <reinauer@google.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/tpm/generic_lpc_tpm.c85
1 files changed, 75 insertions, 10 deletions
diff --git a/drivers/tpm/generic_lpc_tpm.c b/drivers/tpm/generic_lpc_tpm.c
index 6600f7600a7..323238e0c3c 100644
--- a/drivers/tpm/generic_lpc_tpm.c
+++ b/drivers/tpm/generic_lpc_tpm.c
@@ -102,13 +102,31 @@ struct vendor_name {
struct device_name* dev_names;
};
+static struct device_name atmel_devices[] = {
+ {0x3204, "AT97SC3204"},
+ {0xffff}
+};
+
static struct device_name infineon_devices[] = {
- {0xb, "SLB9635 TT 1.2"},
- {0}
+ {0x000b, "SLB9635 TT 1.2"},
+ {0xffff}
+};
+
+static struct device_name nuvoton_devices[] = {
+ {0x00fe, "NPCT420AA V2"},
+ {0xffff}
+};
+
+static struct device_name stmicro_devices[] = {
+ {0x0000, "ST33ZP24" },
+ {0xffff}
};
static const struct vendor_name vendor_names[] = {
+ {0x1114, "Atmel", atmel_devices},
{0x15d1, "Infineon", infineon_devices},
+ {0x1050, "Nuvoton", nuvoton_devices},
+ {0x104a, "ST Microelectronics", stmicro_devices},
};
/*
@@ -183,6 +201,44 @@ static u32 tis_wait_reg(u8 reg, u8 locality, u8 mask, u8 expected)
}
/*
+ * PC Client Specific TPM Interface Specification section 11.2.12:
+ *
+ * Software must be prepared to send two writes of a "1" to command ready
+ * field: the first to indicate successful read of all the data, thus
+ * clearing the data from the ReadFIFO and freeing the TPM's resources,
+ * and the second to indicate to the TPM it is about to send a new command.
+ *
+ * In practice not all TPMs behave the same so it is necessary to be
+ * flexible when trying to set command ready.
+ *
+ * Returns 0 on success if the TPM is ready for transactions.
+ * Returns TPM_TIMEOUT_ERR if the command ready bit does not get set.
+ */
+static int tis_command_ready(u8 locality)
+{
+ u32 status;
+
+ /* 1st attempt to set command ready */
+ tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
+
+ /* Wait for response */
+ status = tpm_read(locality, TIS_REG_STS);
+
+ /* Check if command ready is set yet */
+ if (status & TIS_STS_COMMAND_READY)
+ return 0;
+
+ /* 2nd attempt to set command ready */
+ tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
+
+ /* Wait for command ready to get set */
+ status = tis_wait_reg(TIS_REG_STS, locality,
+ TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
+
+ return (status == TPM_TIMEOUT_ERR) ? TPM_TIMEOUT_ERR : 0;
+}
+
+/*
* Probe the TPM device and try determining its manufacturer/device name.
*
* Returns 0 on success (the device is found or was found during an earlier
@@ -190,15 +246,17 @@ static u32 tis_wait_reg(u8 reg, u8 locality, u8 mask, u8 expected)
*/
static u32 tis_probe(void)
{
- u32 didvid = tpm_read(0, TIS_REG_DID_VID);
- int i;
const char *device_name = "unknown";
const char *vendor_name = device_name;
+ struct device_name *dev;
+ u32 didvid;
u16 vid, did;
+ int i;
if (vendor_dev_id)
return 0; /* Already probed. */
+ didvid = tpm_read(0, TIS_REG_DID_VID);
if (!didvid || (didvid == 0xffffffff)) {
printf("%s: No TPM device found\n", __FUNCTION__);
return TPM_DRIVER_ERR;
@@ -213,11 +271,13 @@ static u32 tis_probe(void)
u16 known_did;
if (vid == vendor_names[i].vendor_id) {
vendor_name = vendor_names[i].vendor_name;
+ } else {
+ continue;
}
- while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) {
+ dev = &vendor_names[i].dev_names[j];
+ while ((known_did = dev->dev_id) != 0xffff) {
if (known_did == did) {
- device_name =
- vendor_names[i].dev_names[j].dev_name;
+ device_name = dev->dev_name;
break;
}
j++;
@@ -225,7 +285,7 @@ static u32 tis_probe(void)
break;
}
/* this will have to be converted into debug printout */
- TPM_DEBUG("Found TPM %s by %s\n", device_name, vendor_name);
+ printf("Found TPM %s by %s\n", device_name, vendor_name);
return 0;
}
@@ -421,7 +481,8 @@ static u32 tis_readresponse(u8 *buffer, u32 *len)
}
/* Tell the TPM that we are done. */
- tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
+ if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
+ return TPM_DRIVER_ERR;
*len = offset;
return 0;
@@ -467,7 +528,11 @@ int tis_open(void)
return TPM_DRIVER_ERR;
}
- tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS);
+ /* Certain TPMs seem to need some delay here or they hang... */
+ udelay(10);
+
+ if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
+ return TPM_DRIVER_ERR;
return 0;
}