mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 16:24:13 +08:00
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:
parent
4cb586a188
commit
d8c3eab5cb
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user