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: Bryan Freed <bfreed@chromium.org>
[rework the patch to adapt to the feedback received]
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Acked-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
This commit is contained in:
Bryan Freed 2017-05-22 11:20:11 +02:00 committed by Jarkko Sakkinen
parent 4cb586a188
commit d8c3eab5cb

View File

@ -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,27 +133,61 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
}
} else {
/* slb9635 protocol should work in all cases */
for (count = 0; count < MAX_COUNT; count++) {
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);
}
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
/* Expect to send one command message and one data message, but
* support looping over each or both if necessary.
*/
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)
break;
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);
if (rc > 0)
break; /* break here to skip sleep */
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
*/
for (count = 0; count < MAX_COUNT; count++) {
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;
}
}