Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Bryan Freed and committed by
Jarkko Sakkinen
d8c3eab5 4cb586a1

+56 -20
+56 -20
drivers/char/tpm/tpm_i2c_infineon.c
··· 70 70 u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ 71 71 struct tpm_chip *chip; 72 72 enum i2c_chip_type chip_type; 73 + unsigned int adapterlimit; 73 74 }; 74 75 75 76 static struct tpm_inf_dev tpm_dev; ··· 112 111 113 112 int rc = 0; 114 113 int count; 114 + unsigned int msglen = len; 115 115 116 116 /* Lock the adapter for the duration of the whole sequence. */ 117 117 if (!tpm_dev.client->adapter->algo->master_xfer) ··· 133 131 usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); 134 132 } 135 133 } else { 136 - /* slb9635 protocol should work in all cases */ 137 - for (count = 0; count < MAX_COUNT; count++) { 138 - rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); 139 - if (rc > 0) 140 - break; /* break here to skip sleep */ 141 - 142 - usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); 143 - } 144 - 145 - if (rc <= 0) 146 - goto out; 147 - 148 - /* After the TPM has successfully received the register address 149 - * it needs some time, thus we're sleeping here again, before 150 - * retrieving the data 134 + /* Expect to send one command message and one data message, but 135 + * support looping over each or both if necessary. 151 136 */ 152 - for (count = 0; count < MAX_COUNT; count++) { 153 - usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); 154 - rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1); 155 - if (rc > 0) 156 - break; 137 + while (len > 0) { 138 + /* slb9635 protocol should work in all cases */ 139 + for (count = 0; count < MAX_COUNT; count++) { 140 + rc = __i2c_transfer(tpm_dev.client->adapter, 141 + &msg1, 1); 142 + if (rc > 0) 143 + break; /* break here to skip sleep */ 144 + 145 + usleep_range(SLEEP_DURATION_LOW, 146 + SLEEP_DURATION_HI); 147 + } 148 + 149 + if (rc <= 0) 150 + goto out; 151 + 152 + /* After the TPM has successfully received the register 153 + * address it needs some time, thus we're sleeping here 154 + * again, before retrieving the data 155 + */ 156 + for (count = 0; count < MAX_COUNT; count++) { 157 + if (tpm_dev.adapterlimit) { 158 + msglen = min_t(unsigned int, 159 + tpm_dev.adapterlimit, 160 + len); 161 + msg2.len = msglen; 162 + } 163 + usleep_range(SLEEP_DURATION_LOW, 164 + SLEEP_DURATION_HI); 165 + rc = __i2c_transfer(tpm_dev.client->adapter, 166 + &msg2, 1); 167 + if (rc > 0) { 168 + /* Since len is unsigned, make doubly 169 + * sure we do not underflow it. 170 + */ 171 + if (msglen > len) 172 + len = 0; 173 + else 174 + len -= msglen; 175 + msg2.buf += msglen; 176 + break; 177 + } 178 + /* If the I2C adapter rejected the request (e.g 179 + * when the quirk read_max_len < len) fall back 180 + * to a sane minimum value and try again. 181 + */ 182 + if (rc == -EOPNOTSUPP) 183 + tpm_dev.adapterlimit = 184 + I2C_SMBUS_BLOCK_MAX; 185 + } 186 + 187 + if (rc <= 0) 188 + goto out; 157 189 } 158 190 } 159 191