/* max. number of iterations after I2C NAK for 'long' commands *weneedthisespeciallyforsendingTPM_READY,sincethecleanupafterthe *transtiontothereadystatemaytakesometime,butitisunpredictable *howlongitwilltake.
*/ #define MAX_COUNT_LONG 50
/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */ #define SLEEP_DURATION_RESET_LOW 2400 #define SLEEP_DURATION_RESET_HI 2600
/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */ #define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000) #define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000)
/* expected value for DIDVID register */ #define TPM_TIS_I2C_DID_VID_9635 0xd1150b00L #define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
struct tpm_inf_dev { struct i2c_client *client; int locality; /* In addition to the data itself, the buffer must fit the 7-bit I2C *addressandthedirectionbit.
*/
u8 buf[TPM_I2C_INFINEON_BUFSIZE + 1]; struct tpm_chip *chip; enum i2c_chip_type chip_type; unsignedint adapterlimit;
};
/* Lock the adapter for the duration of the whole sequence. */ if (!tpm_dev.client->adapter->algo->master_xfer) return -EOPNOTSUPP;
i2c_lock_bus(tpm_dev.client->adapter, I2C_LOCK_SEGMENT);
if (tpm_dev.chip_type == SLB9645) { /* use a combined read for newer chips *unfortunatelythesmbusfunctionsarenotsuitabledueto *the32bytelimitofthesmbus. *retriesshouldusuallynotbeneeded,butarekeptjustto *beonthesafeside.
*/ for (count = 0; count < MAX_COUNT; count++) {
rc = __i2c_transfer(tpm_dev.client->adapter, msgs, 2); if (rc > 0) break; /* break here to skip sleep */
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
}
} else { /* Expect to send one command message and one data message, but *supportloopingovereachorbothifnecessary.
*/ 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 */
/* After the TPM has successfully received the register *addressitneedssometime,thuswe'resleepinghere *again,beforeretrievingthedata
*/ for (count = 0; count < MAX_COUNT; count++) { if (tpm_dev.adapterlimit) {
msglen = min_t(unsignedint,
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 *surewedonotunderflowit.
*/ if (msglen > len)
len = 0; else
len -= msglen;
msg2.buf += msglen; break;
} /* If the I2C adapter rejected the request (e.g *whenthequirkread_max_len<len)fallback *toasaneminimumvalueandtryagain.
*/ if (rc == -EOPNOTSUPP)
tpm_dev.adapterlimit =
I2C_SMBUS_BLOCK_MAX;
}
if (rc <= 0) goto out;
}
}
out:
i2c_unlock_bus(tpm_dev.client->adapter, I2C_LOCK_SEGMENT); /* take care of 'guard time' */
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
/* __i2c_transfer returns the number of successfully transferred *messages. *Sorcshouldbegreaterthan0hereotherwisewehaveanerror.
*/ if (rc <= 0) return -EIO;
return0;
}
staticint iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, unsignedint sleep_low, unsignedint sleep_hi, u8 max_count)
{ int rc = -EIO; int count;
i2c_unlock_bus(tpm_dev.client->adapter, I2C_LOCK_SEGMENT); /* take care of 'guard time' */
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
/* __i2c_transfer returns the number of successfully transferred *messages. *Sorcshouldbegreaterthan0hereotherwisewehaveanerror.
*/ if (rc <= 0) return -EIO;
/* implementation similar to tpm_tis */ staticvoid release_locality(struct tpm_chip *chip, int loc, int force)
{
u8 buf; if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0) return;
/* wait for burstcount */
stop = jiffies + chip->timeout_a; do { if (check_locality(chip, loc)) return loc;
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
} while (time_before(jiffies, stop));
return -ETIME;
}
static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
{ /* NOTE: since I2C read may fail, return 0 in this case --> time-out */
u8 buf = 0xFF;
u8 i = 0;
do { if (iic_tpm_read(TPM_STS(tpm_dev.locality), &buf, 1) < 0) return0;
i++; /* if locallity is set STS should not be 0xFF */
} while ((buf == 0xFF) && i < 10);
return buf;
}
staticvoid tpm_tis_i2c_ready(struct tpm_chip *chip)
{ /* this causes the current command to be aborted */
u8 buf = TPM_STS_COMMAND_READY;
iic_tpm_write_long(TPM_STS(tpm_dev.locality), &buf, 1);
}
/* check current status */
*status = tpm_tis_i2c_status(chip); if ((*status != 0xFF) && (*status & mask) == mask) return0;
stop = jiffies + timeout; do { /* since we just checked the status, give the TPM some time */
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
*status = tpm_tis_i2c_status(chip); if ((*status & mask) == mask) return0;
/* read first 10 bytes, including tag, paramsize, and result */
size = recv_data(chip, buf, TPM_HEADER_SIZE); if (size < TPM_HEADER_SIZE) {
dev_err(&chip->dev, "Unable to read header\n"); goto out;
}
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE); if (size < expected) {
dev_err(&chip->dev, "Unable to read remainder of result\n");
size = -ETIME; goto out;
}
wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status); if (status & TPM_STS_DATA_AVAIL) { /* retry? */
dev_err(&chip->dev, "Error left over data\n");
size = -EIO; goto out;
}
out:
tpm_tis_i2c_ready(chip); /* The TPM needs some time to clean up here, *sowesleepratherthankeepingthebusbusy
*/
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
release_locality(chip, tpm_dev.locality, 0); return size;
}
/* go and do it */
iic_tpm_write(TPM_STS(tpm_dev.locality), &sts, 1);
return0;
out_err:
tpm_tis_i2c_ready(chip); /* The TPM needs some time to clean up here, *sowesleepratherthankeepingthebusbusy
*/
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
release_locality(chip, tpm_dev.locality, 0); return rc;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.