// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2020 Google Inc. * * Based on Infineon TPM driver by Peter Huewe. * * cr50 is a firmware for H1 secure modules that requires special * handling for the I2C interface. * * - Use an interrupt for transaction status instead of hardcoded delays. * - Must use write+wait+read read protocol. * - All 4 bytes of status register must be read/written at once. * - Burst count max is 63 bytes, and burst count behaves slightly differently * than other I2C TPMs. * - When reading from FIFO the full burstcnt must be read instead of just * reading header and determining the remainder.
*/
#define TPM_CR50_MAX_BUFSIZE 64 #define TPM_CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */ #define TPM_CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */ #define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID for Cr50 H1 */ #define TPM_TI50_DT_I2C_DID_VID 0x504a6666L /* Device and vendor ID for Ti50 DT */ #define TPM_TI50_OT_I2C_DID_VID 0x50666666L /* Device and vendor ID for TI50 OT */ #define TPM_CR50_I2C_MAX_RETRIES 3 /* Max retries due to I2C errors */ #define TPM_CR50_I2C_RETRY_DELAY_LO 55 /* Min usecs between retries on I2C */ #define TPM_CR50_I2C_RETRY_DELAY_HI 65 /* Max usecs between retries on I2C */ #define TPM_CR50_I2C_DEFAULT_LOC 0
/** * struct tpm_i2c_cr50_priv_data - Driver private data. * @irq: Irq number used for this chip. * If irq <= 0, then a fixed timeout is used instead of waiting for irq. * @tpm_ready: Struct used by irq handler to signal R/W readiness. * @buf: Buffer used for i2c writes, with i2c address prepended to content. * * Private driver struct used by kernel threads and interrupt context.
*/ struct tpm_i2c_cr50_priv_data { int irq; struct completion tpm_ready;
u8 buf[TPM_CR50_MAX_BUFSIZE];
};
/** * tpm_cr50_i2c_int_handler() - cr50 interrupt handler. * @dummy: Unused parameter. * @tpm_info: TPM chip information. * * The cr50 interrupt handler signals waiting threads that the * interrupt has been asserted. It does not do any interrupt triggered * processing but is instead used to avoid fixed delays. * * Return: * IRQ_HANDLED signifies irq was handled by this device.
*/ static irqreturn_t tpm_cr50_i2c_int_handler(int dummy, void *tpm_info)
{ struct tpm_chip *chip = tpm_info; struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev);
complete(&priv->tpm_ready);
return IRQ_HANDLED;
}
/** * tpm_cr50_i2c_wait_tpm_ready() - Wait for tpm to signal ready. * @chip: A TPM chip. * * Wait for completion interrupt if available, otherwise use a fixed * delay for the TPM to be ready. * * Return: * - 0: Success. * - -errno: A POSIX error code.
*/ staticint tpm_cr50_i2c_wait_tpm_ready(struct tpm_chip *chip)
{ struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev);
/* Use a safe fixed delay if interrupt is not supported */ if (priv->irq <= 0) {
msleep(TPM_CR50_TIMEOUT_NOIRQ_MS); return 0;
}
/* Wait for interrupt to indicate TPM is ready to respond */ if (!wait_for_completion_timeout(&priv->tpm_ready, chip->timeout_a)) {
dev_warn(&chip->dev, "Timeout waiting for TPM ready\n"); return -ETIMEDOUT;
}
/** * tpm_cr50_i2c_transfer_message() - Transfer a message over i2c. * @dev: Device information. * @adapter: I2C adapter. * @msg: Message to transfer. * * Call unlocked i2c transfer routine with the provided parameters and * retry in case of bus errors. * * Return: * - 0: Success. * - -errno: A POSIX error code.
*/ staticint tpm_cr50_i2c_transfer_message(struct device *dev, struct i2c_adapter *adapter, struct i2c_msg *msg)
{ unsignedinttry; int rc;
for (try = 0; try < TPM_CR50_I2C_MAX_RETRIES; try++) {
rc = __i2c_transfer(adapter, msg, 1); if (rc == 1) return 0; /* Successfully transferred the message */ if (try)
dev_warn(dev, "i2c transfer failed (attempt %d/%d): %d\n", try + 1, TPM_CR50_I2C_MAX_RETRIES, rc);
usleep_range(TPM_CR50_I2C_RETRY_DELAY_LO, TPM_CR50_I2C_RETRY_DELAY_HI);
}
/* No i2c message transferred */ return -EIO;
}
/** * tpm_cr50_i2c_read() - Read from TPM register. * @chip: A TPM chip. * @addr: Register address to read from. * @buffer: Read destination, provided by caller. * @len: Number of bytes to read. * * Sends the register address byte to the TPM, then waits until TPM * is ready via interrupt signal or timeout expiration, then 'len' * bytes are read from TPM response into the provided 'buffer'. * * Return: * - 0: Success. * - -errno: A POSIX error code.
*/ staticint tpm_cr50_i2c_read(struct tpm_chip *chip, u8 addr, u8 *buffer, size_t len)
{ struct i2c_client *client = to_i2c_client(chip->dev.parent); struct i2c_msg msg_reg_addr = {
.addr = client->addr,
.len = 1,
.buf = &addr
}; struct i2c_msg msg_response = {
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = buffer
}; int rc;
/* Prepare for completion interrupt */
tpm_cr50_i2c_enable_tpm_irq(chip);
/* Send the register address byte to the TPM */
rc = tpm_cr50_i2c_transfer_message(&chip->dev, client->adapter, &msg_reg_addr); if (rc < 0) goto out;
/* Wait for TPM to be ready with response data */
rc = tpm_cr50_i2c_wait_tpm_ready(chip); if (rc < 0) goto out;
/* Read response data from the TPM */
rc = tpm_cr50_i2c_transfer_message(&chip->dev, client->adapter, &msg_response);
out:
tpm_cr50_i2c_disable_tpm_irq(chip);
if (rc < 0) return rc;
return 0;
}
/** * tpm_cr50_i2c_write()- Write to TPM register. * @chip: A TPM chip. * @addr: Register address to write to. * @buffer: Data to write. * @len: Number of bytes to write. * * The provided address is prepended to the data in 'buffer', the * combined address+data is sent to the TPM, then wait for TPM to * indicate it is done writing. * * Return: * - 0: Success. * - -errno: A POSIX error code.
*/ staticint tpm_cr50_i2c_write(struct tpm_chip *chip, u8 addr, u8 *buffer,
size_t len)
{ struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); struct i2c_msg msg = {
.addr = client->addr,
.len = len + 1,
.buf = priv->buf
}; int rc;
if (len > TPM_CR50_MAX_BUFSIZE - 1) return -EINVAL;
/* Prepend the 'register address' to the buffer */
priv->buf[0] = addr;
memcpy(priv->buf + 1, buffer, len);
/* Prepare for completion interrupt */
tpm_cr50_i2c_enable_tpm_irq(chip);
/* Send write request buffer with address */
rc = tpm_cr50_i2c_transfer_message(&chip->dev, client->adapter, &msg); if (rc < 0) goto out;
/* Wait for TPM to be ready, ignore timeout */
tpm_cr50_i2c_wait_tpm_ready(chip);
out:
tpm_cr50_i2c_disable_tpm_irq(chip);
if (rc < 0) return rc;
return 0;
}
/** * tpm_cr50_check_locality() - Verify if required TPM locality is active. * @chip: A TPM chip. * @loc: Locality to be verified * * Return: * - loc: Success. * - -errno: A POSIX error code.
*/ staticint tpm_cr50_check_locality(struct tpm_chip *chip, int loc)
{
u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY;
u8 buf; int rc;
/** * tpm_cr50_i2c_tis_status() - Read cr50 tis status. * @chip: A TPM chip. * * cr50 requires all 4 bytes of status register to be read. * * Return: * TPM status byte.
*/ static u8 tpm_cr50_i2c_tis_status(struct tpm_chip *chip)
{
u8 buf[4];
if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf)) < 0) return 0;
return buf[0];
}
/** * tpm_cr50_i2c_tis_set_ready() - Set status register to ready. * @chip: A TPM chip. * * cr50 requires all 4 bytes of status register to be written.
*/ staticvoid tpm_cr50_i2c_tis_set_ready(struct tpm_chip *chip)
{
u8 buf[4] = { TPM_STS_COMMAND_READY };
/** * tpm_cr50_i2c_get_burst_and_status() - Get burst count and status. * @chip: A TPM chip. * @mask: Status mask. * @burst: Return value for burst. * @status: Return value for status. * * cr50 uses bytes 3:2 of status register for burst count and * all 4 bytes must be read. * * Return: * - 0: Success. * - -errno: A POSIX error code.
*/ staticint tpm_cr50_i2c_get_burst_and_status(struct tpm_chip *chip, u8 mask,
size_t *burst, u32 *status)
{ unsignedlong stop;
u8 buf[4];
*status = 0;
/* wait for burstcount */
stop = jiffies + chip->timeout_b;
do { if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf)) < 0) {
msleep(TPM_CR50_TIMEOUT_SHORT_MS); continue;
}
/* Read first chunk of burstcnt bytes */
rc = tpm_cr50_i2c_read(chip, addr, buf, burstcnt); if (rc < 0) {
dev_err(&chip->dev, "Read of first chunk failed\n"); goto out_err;
}
/* Determine expected data in the return buffer */
expected = be32_to_cpup((__be32 *)(buf + 2)); if (expected > buf_len) {
dev_err(&chip->dev, "Buffer too small to receive i2c data\n");
rc = -E2BIG; goto out_err;
}
/* Now read the rest of the data */
cur = burstcnt; while (cur < expected) { /* Read updated burst count and check status */
rc = tpm_cr50_i2c_get_burst_and_status(chip, mask, &burstcnt, &status); if (rc < 0) goto out_err;
/* Wait until TPM is ready for a command */
stop = jiffies + chip->timeout_b; while (!(tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)) { if (time_after(jiffies, stop)) {
rc = -ETIMEDOUT; goto out_err;
}
tpm_cr50_i2c_tis_set_ready(chip);
}
while (len > 0) {
u8 mask = TPM_STS_VALID;
/* Wait for data if this is not the first chunk */ if (sent > 0)
mask |= TPM_STS_DATA_EXPECT;
/* Read burst count and check status */
rc = tpm_cr50_i2c_get_burst_and_status(chip, mask, &burstcnt, &status); if (rc < 0) goto out_err;
/* * Use burstcnt - 1 to account for the address byte * that is inserted by tpm_cr50_i2c_write()
*/
limit = min_t(size_t, burstcnt - 1, len);
rc = tpm_cr50_i2c_write(chip, TPM_I2C_DATA_FIFO(chip->locality),
&buf[sent], limit); if (rc < 0) {
dev_err(&chip->dev, "Write failed\n"); goto out_err;
}
sent += limit;
len -= limit;
}
/* Ensure TPM is not expecting more data */
rc = tpm_cr50_i2c_get_burst_and_status(chip, TPM_STS_VALID, &burstcnt, &status); if (rc < 0) goto out_err; if (status & TPM_STS_DATA_EXPECT) {
dev_err(&chip->dev, "Data still expected\n");
rc = -EIO; goto out_err;
}
out_err: /* Abort current transaction if still pending */ if (tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)
tpm_cr50_i2c_tis_set_ready(chip);
return rc;
}
/** * tpm_cr50_i2c_req_canceled() - Callback to notify a request cancel. * @chip: A TPM chip. * @status: Status given by the cancel callback. * * Return: * True if command is ready, False otherwise.
*/ staticbool tpm_cr50_i2c_req_canceled(struct tpm_chip *chip, u8 status)
{ return status == TPM_STS_COMMAND_READY;
}
staticbool tpm_cr50_i2c_is_firmware_power_managed(struct device *dev)
{
u8 val; int ret;
/* This flag should default true when the device property is not present */
ret = device_property_read_u8(dev, "firmware-power-managed", &val); if (ret) returntrue;
/** * tpm_cr50_vid_to_name() - Maps VID to name. * @vendor: Vendor identifier to map to name * * Return: * A valid string for the vendor or empty string
*/ staticconstchar *tpm_cr50_vid_to_name(u32 vendor)
{ switch (vendor) { case TPM_CR50_I2C_DID_VID: return"cr50"; case TPM_TI50_DT_I2C_DID_VID: return"ti50 DT"; case TPM_TI50_OT_I2C_DID_VID: return"ti50 OT"; default: return"unknown";
}
}
priv->irq = client->irq;
} else {
dev_warn(dev, "No IRQ, will use %ums delay for TPM ready\n",
TPM_CR50_TIMEOUT_NOIRQ_MS);
}
loc = tpm_cr50_request_locality(chip, TPM_CR50_I2C_DEFAULT_LOC); if (loc < 0) {
dev_err(dev, "Could not request locality\n"); return loc;
}
/* Read four bytes from DID_VID register */
rc = tpm_cr50_i2c_read(chip, TPM_I2C_DID_VID(loc), buf, sizeof(buf)); if (rc < 0) {
dev_err(dev, "Could not read vendor id\n"); if (tpm_cr50_release_locality(chip, loc))
dev_err(dev, "Could not release locality\n"); return rc;
}
rc = tpm_cr50_release_locality(chip, loc); if (rc) {
dev_err(dev, "Could not release locality\n"); return rc;
}
vendor = le32_to_cpup((__le32 *)buf); if (vendor != TPM_CR50_I2C_DID_VID &&
vendor != TPM_TI50_DT_I2C_DID_VID &&
vendor != TPM_TI50_OT_I2C_DID_VID) {
dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor); return -ENODEV;
}
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.