spi/tpm: claim locality just once during boot

All coreboot stages using TPM start with the same sequence: check if
locality is claimed, if so, release it by writing 'active locality'
bit, then try claiming it.

This is actually not a proper procedure: section "5.5.2.3.1 Command
Aborts" of "TCG PC Client Platform TPM Profile (PTP) Specification
Level 00 Revision 00.430 Family 2" lists overwriting active locality
status bit as a means of triggering TPM command abort.

On top of that, none of the coreboot stages releases locality, it is
enough to claim it once when device starts booting.

In fact, locality being active when the device is in verstage is most
likely due to delayed TPM reset processing by the Cr50 TPM: reset is
an asynchronous event, and is processed once current command
processing completes.

The proper procedure is to wait if locality is active until it is
released (which will happen when Cr50 processes reset) and then
proceed to claim it. This needs to happen only during verstage, other
stages using TPM are guaranteed has been claimed earlier.

BRANCH=gru
BUG=b:65867313

TEST=the new autotest triggering EC reset during key generation
     process does not cause boot failures on Fizz device any more.
     Below are times verstage had to wait:

  TPM ready after 3132 ms
  TPM ready after 22120 ms
  TPM ready after 4936 ms
  TPM ready after 6445 ms
  TPM ready after 11798 ms
  TPM ready after 27421 ms
  TPM ready after 4582 ms
  TPM ready after 7532 ms
  TPM ready after 27920 ms
  TPM ready after 3539 ms
  TPM ready after 12557 ms
  TPM ready after 6773 ms
  TPM ready after 1631 ms
  TPM ready after 197 ms
  TPM ready after 24330 ms
  TPM ready after 3241 ms

Change-Id: Iaee04f009bcde03712483e5e03de4a3441ea32b1
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://review.coreboot.org/22489
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
diff --git a/src/drivers/spi/tpm/tpm.c b/src/drivers/spi/tpm/tpm.c
index c09462e..356c0fe 100644
--- a/src/drivers/spi/tpm/tpm.c
+++ b/src/drivers/spi/tpm/tpm.c
@@ -346,37 +346,48 @@
 	uint8_t access;
 	struct stopwatch sw;
 
-	access = tpm2_read_access_reg();
 	/*
-	 * If active locality is set (maybe reset line is not connected?),
-	 * release the locality and try again.
-	 */
-	if (access & TPM_ACCESS_ACTIVE_LOCALITY) {
-		tpm2_write_access_reg(TPM_ACCESS_ACTIVE_LOCALITY);
-		access = tpm2_read_access_reg();
-	}
-
-	/*
-	 * If cr50 is doing a long crypto operation, it can take up to
-	 * 30 seconds to get a valid status value back
+	 * Locality is released by TPM reset.
+	 *
+	 * If locality is taken at this point, this could be due to the fact
+	 * that the TPM is performing a long operation and has not processed
+	 * reset request yet. We'll wait up to CR50_TIMEOUT_INIT_MS and see if
+	 * it releases locality when reset is processed.
 	 */
 	stopwatch_init_msecs_expire(&sw, CR50_TIMEOUT_INIT_MS);
-	while (!stopwatch_expired(&sw) && access != TPM_ACCESS_VALID)
+	do {
 		access = tpm2_read_access_reg();
-	if (access != TPM_ACCESS_VALID) {
-		printk(BIOS_ERR, "Invalid reset status: %#x\n", access);
-		return 0;
-	}
+		if (access & TPM_ACCESS_ACTIVE_LOCALITY) {
+			/*
+			 * Don't bombard the chip with traffic, let it keep
+			 * processing the command.
+			 */
+			mdelay(2);
+			continue;
+		}
 
-	tpm2_write_access_reg(TPM_ACCESS_REQUEST_USE);
-	access = tpm2_read_access_reg();
-	if (access != (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
-		printk(BIOS_ERR, "Failed to claim locality 0, status: %#x\n",
-		       access);
-		return 0;
-	}
+		/*
+		 * Ok, the locality is free, TPM must be reset, let's claim
+		 * it.
+		 */
 
-	return 1;
+		tpm2_write_access_reg(TPM_ACCESS_REQUEST_USE);
+		access = tpm2_read_access_reg();
+		if (access != (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) {
+			break;
+		}
+
+		printk(BIOS_INFO, "TPM ready after %ld ms\n",
+		       stopwatch_duration_msecs(&sw));
+
+		return 1;
+	} while (!stopwatch_expired(&sw));
+
+	printk(BIOS_ERR,
+	       "Failed to claim locality 0 after %ld ms, status: %#x\n",
+	       stopwatch_duration_msecs(&sw), access);
+
+	return 0;
 }
 
 /* Device/vendor ID values of the TPM devices this driver supports. */
@@ -426,9 +437,13 @@
 
 	printk(BIOS_INFO, " done!\n");
 
-	/* Claim locality 0. */
-	if (!tpm2_claim_locality())
-		return -1;
+	if (ENV_VERSTAGE || ENV_BOOTBLOCK)
+		/*
+		 * Claim locality 0, do it only during the first
+		 * initialization after reset.
+		 */
+		if (!tpm2_claim_locality())
+			return -1;
 
 	read_tpm_sts(&status);
 	if ((status & TPM_STS_FAMILY_MASK) != TPM_STS_FAMILY_TPM_2_0) {