Stefan Reinauer ([email protected]) just uploaded a new patch set to 
gerrit, which you can find at http://review.coreboot.org/972

-gerrit

commit d3884c83a8cc31b3b3bae3eee92e4d429d4c8d52
Author: Stefan Reinauer <[email protected]>
Date:   Mon Apr 30 16:33:44 2012 -0700

    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 <[email protected]>
---
 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 17e1ed7..c7b5081 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;
 }

-- 
coreboot mailing list: [email protected]
http://www.coreboot.org/mailman/listinfo/coreboot

Reply via email to