tpm: read burstcount from TPM_STS in one 32-bit transaction
[pandora-kernel.git] / drivers / char / tpm / tpm_tis.c
index 3f4051a..0c4885d 100644 (file)
@@ -176,16 +176,15 @@ static int get_burstcount(struct tpm_chip *chip)
 {
        unsigned long stop;
        int burstcnt;
+       u32 value;
 
        /* wait for burstcount */
        /* which timeout value, spec has 2 answers (c & d) */
        stop = jiffies + chip->vendor.timeout_d;
        do {
-               burstcnt = ioread8(chip->vendor.iobase +
-                                  TPM_STS(chip->vendor.locality) + 1);
-               burstcnt += ioread8(chip->vendor.iobase +
-                                   TPM_STS(chip->vendor.locality) +
-                                   2) << 8;
+               value = ioread32(chip->vendor.iobase +
+                                TPM_STS(chip->vendor.locality));
+               burstcnt = (value >> 8) & 0xFFFF;
                if (burstcnt)
                        return burstcnt;
                msleep(TPM_TIMEOUT);
@@ -396,6 +395,36 @@ out_err:
        return rc;
 }
 
+struct tis_vendor_timeout_override {
+       u32 did_vid;
+       unsigned long timeout_us[4];
+};
+
+static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
+       /* Atmel 3204 */
+       { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
+                       (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
+};
+
+static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
+                                   unsigned long *timeout_cap)
+{
+       int i;
+       u32 did_vid;
+
+       did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+
+       for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
+               if (vendor_timeout_overrides[i].did_vid != did_vid)
+                       continue;
+               memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
+                      sizeof(vendor_timeout_overrides[i].timeout_us));
+               return true;
+       }
+
+       return false;
+}
+
 /*
  * Early probing for iTPM with STS_DATA_EXPECT flaw.
  * Try sending command without itpm flag set and if that
@@ -432,6 +461,9 @@ static int probe_itpm(struct tpm_chip *chip)
 out:
        itpm = rem_itpm;
        tpm_tis_ready(chip);
+       /* some TPMs need a break here otherwise they will not work
+        * correctly on the immediately subsequent command */
+       msleep(chip->vendor.timeout_b);
        release_locality(chip, chip->vendor.locality, 0);
 
        return rc;
@@ -480,6 +512,7 @@ static struct tpm_vendor_specific tpm_tis = {
        .recv = tpm_tis_recv,
        .send = tpm_tis_send,
        .cancel = tpm_tis_ready,
+       .update_timeouts = tpm_tis_update_timeouts,
        .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
        .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
        .req_canceled = TPM_STS_COMMAND_READY,