Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8c3eab5 authored by Bryan Freed's avatar Bryan Freed Committed by Jarkko Sakkinen
Browse files

tpm: Apply a sane minimum adapterlimit value for retransmission.



When the I2C Infineon part is attached to an I2C adapter that imposes
a size limitation, large requests will fail with -EOPNOTSUPP. Retry
them with a sane minimum size without re-issuing the 0x05 command
as this appears to occasionally put the TPM in a bad state.

Signed-off-by: default avatarBryan Freed <bfreed@chromium.org>
[rework the patch to adapt to the feedback received]
Signed-off-by: default avatarEnric Balletbo i Serra <enric.balletbo@collabora.com>
Acked-by: default avatarAndrew Lunn <andrew@lunn.ch>
Reviewed-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
parent 4cb586a1
Loading
Loading
Loading
Loading
+56 −20
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ struct tpm_inf_dev {
	u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
	struct tpm_chip *chip;
	enum i2c_chip_type chip_type;
	unsigned int adapterlimit;
};

static struct tpm_inf_dev tpm_dev;
@@ -111,6 +112,7 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)

	int rc = 0;
	int count;
	unsigned int msglen = len;

	/* Lock the adapter for the duration of the whole sequence. */
	if (!tpm_dev.client->adapter->algo->master_xfer)
@@ -131,28 +133,62 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
			usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
		}
	} else {
		/* Expect to send one command message and one data message, but
		 * support looping over each or both if necessary.
		 */
		while (len > 0) {
			/* slb9635 protocol should work in all cases */
			for (count = 0; count < MAX_COUNT; count++) {
			rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
				rc = __i2c_transfer(tpm_dev.client->adapter,
						    &msg1, 1);
				if (rc > 0)
					break;	/* break here to skip sleep */

			usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
				usleep_range(SLEEP_DURATION_LOW,
					     SLEEP_DURATION_HI);
			}

			if (rc <= 0)
				goto out;

		/* After the TPM has successfully received the register address
		 * it needs some time, thus we're sleeping here again, before
		 * retrieving the data
			/* After the TPM has successfully received the register
			 * address it needs some time, thus we're sleeping here
			 * again, before retrieving the data
			 */
			for (count = 0; count < MAX_COUNT; count++) {
			usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
			rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1);
			if (rc > 0)
				if (tpm_dev.adapterlimit) {
					msglen = min_t(unsigned int,
						       tpm_dev.adapterlimit,
						       len);
					msg2.len = msglen;
				}
				usleep_range(SLEEP_DURATION_LOW,
					     SLEEP_DURATION_HI);
				rc = __i2c_transfer(tpm_dev.client->adapter,
						    &msg2, 1);
				if (rc > 0) {
					/* Since len is unsigned, make doubly
					 * sure we do not underflow it.
					 */
					if (msglen > len)
						len = 0;
					else
						len -= msglen;
					msg2.buf += msglen;
					break;
				}
				/* If the I2C adapter rejected the request (e.g
				 * when the quirk read_max_len < len) fall back
				 * to a sane minimum value and try again.
				 */
				if (rc == -EOPNOTSUPP)
					tpm_dev.adapterlimit =
							I2C_SMBUS_BLOCK_MAX;
			}

			if (rc <= 0)
				goto out;
		}
	}

out: