diff options
author | Stefan Reinauer <reinauer@chromium.org> | 2012-04-30 16:33:44 -0700 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2012-05-01 20:06:32 +0200 |
commit | c908fc762cfb3dea096b2805c8cbe9a831b11585 (patch) | |
tree | fd0f639bead56947838cb42b27c688b3c561b74d | |
parent | 95be1d6f469a552657bc6a63ccdb27a7654f810f (diff) |
Fix TPM driver to work with multiple vendor TPMs
Port u-boot patch for low-level driver:
- 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.
Change-Id: I57c280266b2e966c5b90e4f9e968426a33b93cf1
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: http://review.coreboot.org/972
Tested-by: build bot (Jenkins)
-rw-r--r-- | src/drivers/pc80/tpm.c | 85 |
1 files changed, 75 insertions, 10 deletions
diff --git a/src/drivers/pc80/tpm.c b/src/drivers/pc80/tpm.c index 17e1ed702d..c7b50816be 100644 --- a/src/drivers/pc80/tpm.c +++ b/src/drivers/pc80/tpm.c @@ -127,13 +127,31 @@ struct vendor_name { const struct device_name* dev_names; }; +static const struct device_name atmel_devices[] = { + {0x3204, "AT97SC3204"}, + {0xffff} +}; + static const struct device_name infineon_devices[] = { - {0xb, "SLB9635 TT 1.2"}, - {0} + {0x000b, "SLB9635 TT 1.2"}, + {0xffff} +}; + +static const struct device_name nuvoton_devices[] = { + {0x00fe, "NPCT420AA V2"}, + {0xffff} +}; + +static const 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}, }; /* @@ -208,6 +226,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 @@ -215,15 +271,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; + const 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; @@ -238,11 +296,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++; @@ -250,7 +310,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; } @@ -446,7 +506,8 @@ static u32 tis_readresponse(u8 *buffer, size_t *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; @@ -492,7 +553,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; } |