drivers/char/tpm/tpm_i2c_infineon.c

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

File Facts

System
Linux kernel
Corpus path
drivers/char/tpm/tpm_i2c_infineon.c
Extension
.c
Size
18371 bytes
Lines
736
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_inf_dev {
	struct i2c_client *client;
	int locality;
	/* In addition to the data itself, the buffer must fit the 7-bit I2C
	 * address and the direction bit.
	 */
	u8 buf[TPM_I2C_INFINEON_BUFSIZE + 1];
	struct tpm_chip *chip;
	enum i2c_chip_type chip_type;
	unsigned int adapterlimit;
};

static struct tpm_inf_dev tpm_dev;

/*
 * iic_tpm_read() - read from TPM register
 * @addr: register address to read from
 * @buffer: provided by caller
 * @len: number of bytes to read
 *
 * Read len bytes from TPM register and put them into
 * buffer (little-endian format, i.e. first byte is put into buffer[0]).
 *
 * NOTE: TPM is big-endian for multi-byte values. Multi-byte
 * values have to be swapped.
 *
 * NOTE: We can't unfortunately use the combined read/write functions
 * provided by the i2c core as the TPM currently does not support the
 * repeated start condition and due to it's special requirements.
 * The i2c_smbus* functions do not work for this chip.
 *
 * Return -EIO on error, 0 on success.
 */
static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
{

	struct i2c_msg msg1 = {
		.addr = tpm_dev.client->addr,
		.len = 1,
		.buf = &addr
	};
	struct i2c_msg msg2 = {
		.addr = tpm_dev.client->addr,
		.flags = I2C_M_RD,
		.len = len,
		.buf = buffer
	};
	struct i2c_msg msgs[] = {msg1, msg2};

	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)
		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
		 * unfortunately the smbus functions are not suitable due to
		 * the 32 byte limit of the smbus.
		 * retries should usually not be needed, but are kept just to
		 * be on the safe side.
		 */
		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
		 * support looping over each or both if necessary.
		 */
		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;

Annotation

Implementation Notes