drivers/char/tpm/tpm_tis_i2c.c

Source file repositories/reference/linux-study-clean/drivers/char/tpm/tpm_tis_i2c.c

File Facts

System
Linux kernel
Corpus path
drivers/char/tpm/tpm_tis_i2c.c
Extension
.c
Size
10874 bytes
Lines
407
Domain
Driver Families
Bucket
drivers/char
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct tpm_tis_i2c_phy {
	struct tpm_tis_data priv;
	struct i2c_client *i2c_client;
	bool guard_time_read;
	bool guard_time_write;
	u16 guard_time_min;
	u16 guard_time_max;
	u8 *io_buf;
};

static inline struct tpm_tis_i2c_phy *
to_tpm_tis_i2c_phy(struct tpm_tis_data *data)
{
	return container_of(data, struct tpm_tis_i2c_phy, priv);
}

/*
 * tpm_tis_core uses the register addresses as defined in Table 19 "Allocation
 * of Register Space for FIFO TPM Access" of the TCG PC Client PTP
 * Specification. In order for this code to work together with tpm_tis_core,
 * those addresses need to mapped to the registers defined for I2C TPMs in
 * Table 51 "I2C-TPM Register Overview".
 *
 * For most addresses this can be done by simply stripping off the locality
 * information from the address. A few addresses need to be mapped explicitly,
 * since the corresponding I2C registers have been moved around. TPM_LOC_SEL is
 * only defined for I2C TPMs and is also mapped explicitly here to distinguish
 * it from TPM_ACCESS(0).
 *
 * Locality information is ignored, since this driver assumes exclusive access
 * to the TPM and always uses locality 0.
 */
static u8 tpm_tis_i2c_address_to_register(u32 addr)
{
	addr &= TPM_TIS_REGISTER_MASK;

	switch (addr) {
	case TPM_ACCESS(0):
		return TPM_I2C_ACCESS;
	case TPM_LOC_SEL:
		return TPM_I2C_LOC_SEL;
	case TPM_DID_VID(0):
		return TPM_I2C_DID_VID;
	case TPM_RID(0):
		return TPM_I2C_RID;
	default:
		return addr;
	}
}

static int tpm_tis_i2c_retry_transfer_until_ack(struct tpm_tis_data *data,
						struct i2c_msg *msg)
{
	struct tpm_tis_i2c_phy *phy = to_tpm_tis_i2c_phy(data);
	bool guard_time;
	int i = 0;
	int ret;

	if (msg->flags & I2C_M_RD)
		guard_time = phy->guard_time_read;
	else
		guard_time = phy->guard_time_write;

	do {
		ret = i2c_transfer(phy->i2c_client->adapter, msg, 1);
		if (ret < 0)
			usleep_range(GUARD_TIME_ERR_MIN, GUARD_TIME_ERR_MAX);
		else if (guard_time)
			usleep_range(phy->guard_time_min, phy->guard_time_max);
		/* retry on TPM NACK */
	} while (ret < 0 && i++ < TPM_RETRY);

	return ret;
}

/* Check that bits which must be read zero are not set */
static int tpm_tis_i2c_sanity_check_read(u8 reg, u16 len, u8 *buf)
{
	u32 zero_mask;
	u32 value;

	switch (len) {
	case sizeof(u8):
		value = buf[0];
		break;
	case sizeof(u16):
		value = le16_to_cpup((__le16 *)buf);
		break;
	case sizeof(u32):
		value = le32_to_cpup((__le32 *)buf);

Annotation

Implementation Notes