All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH u-boot v2019.04-aspeed-openbmc 0/5] ast2600: Add I2C TPMv2 driver
@ 2022-05-05 20:28 Eddie James
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 1/5] i2c: ast_i2c: Remove SCL direct drive mode Eddie James
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Eddie James @ 2022-05-05 20:28 UTC (permalink / raw)
  To: openbmc; +Cc: Eddie James, joel

This series fixes the AST2XXX I2C driver to work with clock-stretching
devices and adds a TPMv2 driver for the NPCT75X device. Enable the TPM on
the Rainier system and add a board init function to write PCR0 of the TPM.

Eddie James (5):
  i2c: ast_i2c: Remove SCL direct drive mode
  tpm: Add I2C driver for TPMv2 devices
  arm: dts: ast2600-rainier: Add NPCT75X TPM
  configs: ast2600_openbmc_spl_emmc: Enable TPMv2 over I2C
  aspeed: Add board_late_init to write TPM

 arch/arm/dts/ast2600-rainier.dts           |  12 +-
 board/aspeed/evb_ast2600/evb_ast2600.c     |  34 ++
 configs/ast2600_openbmc_spl_emmc_defconfig |   3 +-
 drivers/i2c/ast_i2c.c                      |   2 +-
 drivers/tpm/Kconfig                        |   9 +
 drivers/tpm/Makefile                       |   1 +
 drivers/tpm/tpm2_tis_i2c.c                 | 593 +++++++++++++++++++++
 7 files changed, 647 insertions(+), 7 deletions(-)
 create mode 100644 drivers/tpm/tpm2_tis_i2c.c

-- 
2.27.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH u-boot v2019.04-aspeed-openbmc 1/5] i2c: ast_i2c: Remove SCL direct drive mode
  2022-05-05 20:28 [PATCH u-boot v2019.04-aspeed-openbmc 0/5] ast2600: Add I2C TPMv2 driver Eddie James
@ 2022-05-05 20:28 ` Eddie James
  2022-05-10  2:29   ` Joel Stanley
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 2/5] tpm: Add I2C driver for TPMv2 devices Eddie James
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Eddie James @ 2022-05-05 20:28 UTC (permalink / raw)
  To: openbmc; +Cc: Eddie James, joel

SCL direct drive mode prevents communication with devices that
do clock stretching, so disable. The Linux driver doesn't use
this mode, and the engine can handle clock stretching.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/i2c/ast_i2c.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/ast_i2c.c b/drivers/i2c/ast_i2c.c
index bbc32d6cdb..13420ade84 100644
--- a/drivers/i2c/ast_i2c.c
+++ b/drivers/i2c/ast_i2c.c
@@ -73,7 +73,7 @@ static void ast_i2c_init_bus(struct udevice *dev)
 	/* Enable Master Mode. Assuming single-master */
 	writel(I2CD_MASTER_EN
 	       | I2CD_M_SDA_LOCK_EN
-	       | I2CD_MULTI_MASTER_DIS | I2CD_M_SCL_DRIVE_EN,
+	       | I2CD_MULTI_MASTER_DIS,
 	       &priv->regs->fcr);
 	/* Enable Interrupts */
 	writel(I2CD_INTR_TX_ACK
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH u-boot v2019.04-aspeed-openbmc 2/5] tpm: Add I2C driver for TPMv2 devices
  2022-05-05 20:28 [PATCH u-boot v2019.04-aspeed-openbmc 0/5] ast2600: Add I2C TPMv2 driver Eddie James
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 1/5] i2c: ast_i2c: Remove SCL direct drive mode Eddie James
@ 2022-05-05 20:28 ` Eddie James
  2022-05-10  2:42   ` Joel Stanley
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 3/5] arm: dts: ast2600-rainier: Add NPCT75X TPM Eddie James
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Eddie James @ 2022-05-05 20:28 UTC (permalink / raw)
  To: openbmc; +Cc: Eddie James, joel

Add a driver to communicate with TPMv2 chips over I2C, such
as the NPCT75X.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/tpm/Kconfig        |   9 +
 drivers/tpm/Makefile       |   1 +
 drivers/tpm/tpm2_tis_i2c.c | 593 +++++++++++++++++++++++++++++++++++++
 3 files changed, 603 insertions(+)
 create mode 100644 drivers/tpm/tpm2_tis_i2c.c

diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
index 94629dffd2..6fd98ac057 100644
--- a/drivers/tpm/Kconfig
+++ b/drivers/tpm/Kconfig
@@ -145,6 +145,15 @@ config TPM2_TIS_SPI
 	  to the device using the standard TPM Interface Specification (TIS)
 	  protocol.
 
+config TPM2_TIS_I2C
+	bool "Enable support for TPMv2.x I2C chips"
+	depends on TPM_V2 && DM_I2C
+	help
+	  This driver supports TPMv2.x devices connected on the I2C bus.
+	  The usual TPM operations and the 'tpm' command can be used to talk
+	  to the device using the standard TPM Interface Specification (TIS)
+	  protocol.
+
 endif # TPM_V2
 
 endmenu
diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
index 94c337b8ed..220f03253c 100644
--- a/drivers/tpm/Makefile
+++ b/drivers/tpm/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
 
 obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
 obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
+obj-$(CONFIG_TPM2_TIS_I2C) += tpm2_tis_i2c.o
diff --git a/drivers/tpm/tpm2_tis_i2c.c b/drivers/tpm/tpm2_tis_i2c.c
new file mode 100644
index 0000000000..5fab9122e6
--- /dev/null
+++ b/drivers/tpm/tpm2_tis_i2c.c
@@ -0,0 +1,593 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <tpm-v1.h>
+#include <linux/errno.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/unaligned/be_byteshift.h>
+
+#include "tpm_tis.h"
+#include "tpm_internal.h"
+
+enum i2c_chip_type {
+	NPCT75X,
+	UNKNOWN,
+};
+
+/* expected value for DIDVID register */
+#define TPM2_TIS_I2C_DID_VID_NPCT75X 0x5010FC00L
+
+static const char * const chip_name[] = {
+	[NPCT75X] = "npct75X",
+	[UNKNOWN] = "unknown/fallback to npct75X",
+};
+
+#define TPM_LOC_SEL		0x00
+#define	TPM_ACCESS		0x04
+#define	TPM_STS			0x18
+#define	TPM_DATA_FIFO		0x24
+#define	TPM_DID_VID		0x48
+
+/*
+ * tpm2_tis_i2c_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.
+ *
+ * Return -EIO on error, 0 on success.
+ */
+static int tpm2_tis_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
+			     size_t len)
+{
+	int rc;
+	int count;
+
+	for (count = 0; count < MAX_COUNT; count++) {
+		rc = dm_i2c_read(dev, addr, buffer, len);
+		if (rc == 0) {
+			debug("read addr[%02x] len[%u] data[%02x %02x %02x %02x%s]\n", addr, len, len > 0 ? buffer[0] : 0, len > 1 ? buffer[1] : 0, len > 2 ? buffer[2] : 0, len > 3 ? buffer[3] : 0, len > 4 ? " ..." : "");
+			break;  /* break here to skip sleep */
+		}
+		udelay(SLEEP_DURATION_US);
+	}
+
+	/* Take care of 'guard time' */
+	udelay(SLEEP_DURATION_US);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int tpm2_tis_i2c_write_generic(struct udevice *dev, u8 addr,
+				      const u8 *buffer, size_t len,
+				      unsigned int sleep_time_us, u8 max_count)
+{
+	int rc = 0;
+	int count;
+
+	for (count = 0; count < max_count; count++) {
+		rc = dm_i2c_write(dev, addr, buffer, len);
+		if (rc == 0) {
+			debug("write addr[%02x] len[%u] data[%02x %02x %02x %02x%s]\n", addr, len, len > 0 ? buffer[0] : 0, len > 1 ? buffer[1] : 0, len > 2 ? buffer[2] : 0, len > 3 ? buffer[3] : 0, len > 4 ? " ..." : "");
+			break;  /* Success, break to skip sleep */
+		}
+		udelay(sleep_time_us);
+	}
+
+	/* take care of 'guard time' */
+	udelay(sleep_time_us);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+/*
+ * tpm2_tis_i2c_write() - write to TPM register
+ * @addr: register address to write to
+ * @buffer: containing data to be written
+ * @len: number of bytes to write
+ *
+ * Write len bytes from provided buffer to TPM register (little
+ * endian format, i.e. buffer[0] is written as first byte).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * NOTE: use this function instead of the tpm2_tis_i2c_write_generic function.
+ *
+ * Return -EIO on error, 0 on success
+ */
+static int tpm2_tis_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
+			      size_t len)
+{
+	return tpm2_tis_i2c_write_generic(dev, addr, buffer, len,
+					  SLEEP_DURATION_US, MAX_COUNT);
+}
+
+/*
+ * This function is needed especially for the cleanup situation after
+ * sending TPM_READY
+ */
+static int tpm2_tis_i2c_write_long(struct udevice *dev, u8 addr, u8 *buffer,
+				   size_t len)
+{
+	return tpm2_tis_i2c_write_generic(dev, addr, buffer, len,
+					  SLEEP_DURATION_LONG_US,
+					  MAX_COUNT_LONG);
+}
+
+static int tpm2_tis_i2c_check_locality(struct udevice *dev, int loc)
+{
+	const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
+	struct tpm_chip *chip = dev_get_priv(dev);
+	u8 buf;
+	int rc;
+
+	buf = loc;
+	rc = tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1);
+	if (rc < 0)
+		return rc;
+
+	rc = tpm2_tis_i2c_read(dev, TPM_ACCESS, &buf, 1);
+	if (rc < 0)
+		return rc;
+
+	if ((buf & mask) == mask) {
+		chip->locality = loc;
+		return loc;
+	}
+
+	return -ENOENT;
+}
+
+static void tpm2_tis_i2c_release_locality(struct udevice *dev, int loc,
+					 int force)
+{
+	const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
+	u8 buf;
+
+	buf = loc;
+	if (tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1) < 0)
+		return;
+
+	if (tpm2_tis_i2c_read(dev, TPM_ACCESS, &buf, 1) < 0)
+		return;
+
+	if (force || (buf & mask) == mask) {
+		buf = TPM_ACCESS_ACTIVE_LOCALITY;
+		tpm2_tis_i2c_write(dev, TPM_ACCESS, &buf, 1);
+	}
+}
+
+static int tpm2_tis_i2c_request_locality(struct udevice *dev, int loc)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+	unsigned long start, stop;
+	u8 buf = 0;
+	int rc;
+
+	rc = tpm2_tis_i2c_check_locality(dev, loc);
+	if (rc >= 0) {
+		debug("%s: Already have locality\n", __func__);
+		return loc;  /* We already have the locality */
+	} else if (rc != -ENOENT) {
+		debug("%s: Failed to get locality: %d\n", __func__, rc);
+		return rc;
+	}
+
+	buf = loc;
+	rc = tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1);
+	if (rc) {
+		debug("%s: Failed to write to TPM: %d\n", __func__, rc);
+		return rc;
+	}
+
+	buf = TPM_ACCESS_REQUEST_USE;
+	rc = tpm2_tis_i2c_write(dev, TPM_ACCESS, &buf, 1);
+	if (rc) {
+		debug("%s: Failed to write to TPM: %d\n", __func__, rc);
+		return rc;
+	}
+
+	/* Wait for burstcount */
+	start = get_timer(0);
+	stop = chip->timeout_a;
+	do {
+		rc = tpm2_tis_i2c_check_locality(dev, loc);
+		if (rc >= 0) {
+			debug("%s: Have locality\n", __func__);
+			return loc;
+		} else if (rc != -ENOENT) {
+			debug("%s: Failed to get locality: %d\n", __func__, rc);
+			return rc;
+		}
+		mdelay(TPM_TIMEOUT_MS);
+	} while (get_timer(start) < stop);
+	debug("%s: Timeout getting locality: %d\n", __func__, rc);
+
+	return rc;
+}
+
+static u8 tpm2_tis_i2c_status(struct udevice *dev)
+{
+	/* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
+	u8 buf;
+
+	if (tpm2_tis_i2c_read(dev, TPM_STS, &buf, 1) < 0)
+		return 0;
+	else
+		return buf;
+}
+
+static int tpm2_tis_i2c_ready(struct udevice *dev)
+{
+	int rc;
+
+	/* This causes the current command to be aborted */
+	u8 buf = TPM_STS_COMMAND_READY;
+
+	debug("%s\n", __func__);
+	rc = tpm2_tis_i2c_write_long(dev, TPM_STS, &buf, 1);
+	if (rc)
+		debug("%s: rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+static ssize_t tpm2_tis_i2c_get_burstcount(struct udevice *dev)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+	unsigned long start, stop;
+	ssize_t burstcnt;
+	u8 addr, buf[3];
+
+	/* Wait for burstcount */
+	/* XXX: Which timeout value? Spec has 2 answers (c & d) */
+	start = get_timer(0);
+	stop = chip->timeout_d;
+	do {
+		/* Note: STS is little endian */
+		addr = TPM_STS + 1;
+		if (tpm2_tis_i2c_read(dev, addr, buf, 2) < 0)
+			burstcnt = 0;
+		else
+			burstcnt = (buf[1] << 8) + buf[0];
+
+		if (burstcnt)
+			return burstcnt;
+		mdelay(TPM_TIMEOUT_MS);
+	} while (get_timer(start) < stop);
+
+	return -EBUSY;
+}
+
+static int tpm2_tis_i2c_wait_for_stat(struct udevice *dev, u8 mask,
+				      unsigned long timeout, int *status)
+{
+	unsigned long start, stop;
+
+	/* Check current status */
+	*status = tpm2_tis_i2c_status(dev);
+	if ((*status & mask) == mask)
+		return 0;
+
+	start = get_timer(0);
+	stop = timeout;
+	do {
+		mdelay(TPM_TIMEOUT_MS);
+		*status = tpm2_tis_i2c_status(dev);
+		if ((*status & mask) == mask)
+			return 0;
+	} while (get_timer(start) < stop);
+
+	return -ETIMEDOUT;
+}
+
+static int tpm2_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
+{
+	size_t size = 0;
+	ssize_t burstcnt;
+	int rc;
+
+	while (size < count) {
+		burstcnt = tpm2_tis_i2c_get_burstcount(dev);
+
+		/* burstcount < 0 -> tpm is busy */
+		if (burstcnt < 0)
+			return burstcnt;
+
+		/* Limit received data to max left */
+		if (burstcnt > (count - size))
+			burstcnt = count - size;
+
+		rc = tpm2_tis_i2c_read(dev, TPM_DATA_FIFO,
+				       &(buf[size]), burstcnt);
+		if (rc == 0)
+			size += burstcnt;
+	}
+
+	return size;
+}
+
+static int tpm2_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+	int size = 0;
+	int status;
+	unsigned int expected;
+	int rc;
+
+	status = tpm2_tis_i2c_status(dev);
+	if (status == TPM_STS_COMMAND_READY)
+		return -EINTR;
+	if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) !=
+	    (TPM_STS_DATA_AVAIL | TPM_STS_VALID))
+		return -EAGAIN;
+
+	debug("...got it;\n");
+
+	/* Read first 10 bytes, including tag, paramsize, and result */
+	size = tpm2_tis_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
+	if (size < TPM_HEADER_SIZE) {
+		debug("Unable to read header\n");
+		return size < 0 ? size : -EIO;
+	}
+
+	expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
+	if ((size_t)expected > count || (size_t)expected < TPM_HEADER_SIZE) {
+		debug("Error size=%x, expected=%x, count=%x\n", size, expected,
+		      count);
+		return -ENOSPC;
+	}
+
+	size += tpm2_tis_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
+				       expected - TPM_HEADER_SIZE);
+	if (size < expected) {
+		debug("Unable to read remainder of result\n");
+		return -ETIMEDOUT;
+	}
+
+	rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c,
+					&status);
+	if (rc)
+		return rc;
+	if (status & TPM_STS_DATA_AVAIL) {  /* Retry? */
+		debug("Error left over data\n");
+		return -EIO;
+	}
+
+	return size;
+}
+
+static int tpm2_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+	int rc, status;
+	size_t burstcnt;
+	size_t count = 0;
+	int retry = 0;
+	u8 sts = TPM_STS_GO;
+
+	debug("%s: len=%d\n", __func__, len);
+	if (len > TPM_DEV_BUFSIZE)
+		return -E2BIG;  /* Command is too long for our tpm, sorry */
+
+	if (tpm2_tis_i2c_request_locality(dev, 0) < 0)
+		return -EBUSY;
+
+	status = tpm2_tis_i2c_status(dev);
+	if ((status & TPM_STS_COMMAND_READY) == 0) {
+		rc = tpm2_tis_i2c_ready(dev);
+		if (rc)
+			return rc;
+		rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
+						chip->timeout_b, &status);
+		if (rc)
+			return rc;
+	}
+
+	burstcnt = tpm2_tis_i2c_get_burstcount(dev);
+
+	/* burstcount < 0 -> tpm is busy */
+	if (burstcnt < 0)
+		return burstcnt;
+
+	while (count < len) {
+		udelay(300);
+		if (burstcnt > len - count)
+			burstcnt = len - count;
+
+#ifdef CONFIG_TPM2_TIS_I2C_BURST_LIMITATION
+		if (retry && burstcnt > CONFIG_TPM2_TIS_I2C_BURST_LIMITATION_LEN)
+			burstcnt = CONFIG_TPM2_TIS_I2C_BURST_LIMITATION_LEN;
+#endif /* CONFIG_TPM2_TIS_I2C_BURST_LIMITATION */
+
+		rc = tpm2_tis_i2c_write(dev, TPM_DATA_FIFO,
+					&(buf[count]), burstcnt);
+		if (rc == 0)
+			count += burstcnt;
+		else {
+			debug("%s: error\n", __func__);
+			if (retry++ > 10)
+				return -EIO;
+			rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_VALID,
+							chip->timeout_c,
+							&status);
+			if (rc)
+				return rc;
+
+			if ((status & TPM_STS_DATA_EXPECT) == 0)
+				return -EIO;
+		}
+	}
+
+	/* Go and do it */
+	rc = tpm2_tis_i2c_write(dev, TPM_STS, &sts, 1);
+	if (rc < 0)
+		return rc;
+	debug("%s: done, rc=%d\n", __func__, rc);
+
+	return len;
+}
+
+static int tpm2_tis_i2c_cleanup(struct udevice *dev)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+
+	tpm2_tis_i2c_ready(dev);
+	/*
+	 * The TPM needs some time to clean up here,
+	 * so we sleep rather than keeping the bus busy
+	 */
+	mdelay(2);
+	tpm2_tis_i2c_release_locality(dev, chip->locality, 0);
+
+	return 0;
+}
+
+static int tpm2_tis_i2c_init(struct udevice *dev)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+	u32 vendor;
+	u32 expected_did_vid;
+	int rc;
+
+	chip->is_open = 1;
+
+	/* Default timeouts - these could move to the device tree */
+	chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
+	chip->timeout_b = TIS_LONG_TIMEOUT_MS;
+	chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
+	chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
+
+	rc = tpm2_tis_i2c_request_locality(dev, 0);
+	if (rc < 0)
+		return rc;
+
+	/* Read four bytes from DID_VID register */
+	if (tpm2_tis_i2c_read(dev, TPM_DID_VID, (uchar *)&vendor, 4) < 0) {
+		tpm2_tis_i2c_release_locality(dev, 0, 1);
+		return -EIO;
+	}
+
+	if (chip->chip_type == NPCT75X) {
+		vendor = be32_to_cpu(vendor);
+		expected_did_vid = TPM2_TIS_I2C_DID_VID_NPCT75X;
+	}
+
+	if (chip->chip_type != UNKNOWN && vendor != expected_did_vid) {
+		pr_err("Vendor id did not match! ID was %08x\n", vendor);
+		return -ENODEV;
+	}
+
+	chip->vend_dev = vendor;
+	debug("2.0 TPM (chip type %s device-id 0x%X)\n",
+	      chip_name[chip->chip_type], vendor >> 16);
+
+	/*
+	 * A timeout query to TPM can be placed here.
+	 * Standard timeout values are used so far
+	 */
+
+	return 0;
+}
+
+static int tpm2_tis_i2c_open(struct udevice *dev)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+	int rc;
+
+	debug("%s: start\n", __func__);
+	if (chip->is_open)
+		return -EBUSY;
+	rc = tpm2_tis_i2c_init(dev);
+	if (rc < 0)
+		chip->is_open = 0;
+
+	return rc;
+}
+
+static int tpm2_tis_i2c_close(struct udevice *dev)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+
+	if (chip->is_open) {
+		tpm2_tis_i2c_release_locality(dev, chip->locality, 1);
+		chip->is_open = 0;
+		chip->vend_dev = 0;
+	}
+
+	return 0;
+}
+
+static int tpm2_tis_get_desc(struct udevice *dev, char *buf, int size)
+{
+	struct tpm_chip *chip = dev_get_priv(dev);
+
+	if (size < 50)
+		return -ENOSPC;
+
+	return snprintf(buf, size, "2.0 TPM (%s, chip type %s device-id 0x%x)",
+			chip->is_open ? "open" : "closed",
+			chip_name[chip->chip_type],
+			chip->vend_dev >> 16);
+}
+
+static int tpm2_tis_i2c_probe(struct udevice *dev)
+{
+	struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct tpm_chip *chip = dev_get_priv(dev);
+
+	chip->chip_type = dev_get_driver_data(dev);
+	chip->locality = 0;
+	chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
+	chip->timeout_b = TIS_LONG_TIMEOUT_MS;
+	chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
+	chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
+
+	/* TODO: These need to be checked and tuned */
+	uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
+	uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
+	uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
+	uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
+	uc_priv->pcr_count = 24;
+	uc_priv->pcr_select_min = 3;
+	uc_priv->version = TPM_V2;
+
+	return 0;
+}
+
+static const struct tpm_ops tpm2_tis_i2c_ops = {
+	.open		= tpm2_tis_i2c_open,
+	.close		= tpm2_tis_i2c_close,
+	.get_desc	= tpm2_tis_get_desc,
+	.send		= tpm2_tis_i2c_send,
+	.recv		= tpm2_tis_i2c_recv,
+	.cleanup	= tpm2_tis_i2c_cleanup,
+};
+
+static const struct udevice_id tpm2_tis_i2c_ids[] = {
+	{ .compatible = "nuvoton,npct75x", .data = NPCT75X },
+	{ }
+};
+
+U_BOOT_DRIVER(tpm2_tis_i2c) = {
+	.name   = "tpm2_tis_i2c",
+	.id     = UCLASS_TPM,
+	.of_match = tpm2_tis_i2c_ids,
+	.ops    = &tpm2_tis_i2c_ops,
+	.probe	= tpm2_tis_i2c_probe,
+	.priv_auto_alloc_size = sizeof(struct tpm_chip),
+};
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH u-boot v2019.04-aspeed-openbmc 3/5] arm: dts: ast2600-rainier: Add NPCT75X TPM
  2022-05-05 20:28 [PATCH u-boot v2019.04-aspeed-openbmc 0/5] ast2600: Add I2C TPMv2 driver Eddie James
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 1/5] i2c: ast_i2c: Remove SCL direct drive mode Eddie James
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 2/5] tpm: Add I2C driver for TPMv2 devices Eddie James
@ 2022-05-05 20:28 ` Eddie James
  2022-05-10  2:44   ` Joel Stanley
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 4/5] configs: ast2600_openbmc_spl_emmc: Enable TPMv2 over I2C Eddie James
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 5/5] aspeed: Add board_late_init to write TPM Eddie James
  4 siblings, 1 reply; 12+ messages in thread
From: Eddie James @ 2022-05-05 20:28 UTC (permalink / raw)
  To: openbmc; +Cc: Eddie James, joel

Add the TPM device on I2C bus 12.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 arch/arm/dts/ast2600-rainier.dts | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/arch/arm/dts/ast2600-rainier.dts b/arch/arm/dts/ast2600-rainier.dts
index 8a44fd8a0a..aa91b12ed3 100755
--- a/arch/arm/dts/ast2600-rainier.dts
+++ b/arch/arm/dts/ast2600-rainier.dts
@@ -106,12 +106,14 @@
 
 &gpio0 {
 	u-boot,dm-pre-reloc;
+};
+
+&i2c12 {
+	status = "okay";
 
-	tpm_reset {
-		u-boot,dm-pre-reloc;
-		gpio-hog;
-		output-high;
-		gpios = <ASPEED_GPIO(R, 0) GPIO_ACTIVE_HIGH>;
+	tpm@2e {
+		compatible = "nuvoton,npct75x";
+		reg = <0x2e>;
 	};
 };
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH u-boot v2019.04-aspeed-openbmc 4/5] configs: ast2600_openbmc_spl_emmc: Enable TPMv2 over I2C
  2022-05-05 20:28 [PATCH u-boot v2019.04-aspeed-openbmc 0/5] ast2600: Add I2C TPMv2 driver Eddie James
                   ` (2 preceding siblings ...)
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 3/5] arm: dts: ast2600-rainier: Add NPCT75X TPM Eddie James
@ 2022-05-05 20:28 ` Eddie James
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 5/5] aspeed: Add board_late_init to write TPM Eddie James
  4 siblings, 0 replies; 12+ messages in thread
From: Eddie James @ 2022-05-05 20:28 UTC (permalink / raw)
  To: openbmc; +Cc: Eddie James, joel

Enable the I2C TPM communication driver to talk to TPM device
over I2C.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 configs/ast2600_openbmc_spl_emmc_defconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/configs/ast2600_openbmc_spl_emmc_defconfig b/configs/ast2600_openbmc_spl_emmc_defconfig
index 4ea0f34148..3bb44280c7 100644
--- a/configs/ast2600_openbmc_spl_emmc_defconfig
+++ b/configs/ast2600_openbmc_spl_emmc_defconfig
@@ -132,5 +132,5 @@ CONFIG_WDT=y
 CONFIG_USE_TINY_PRINTF=y
 CONFIG_SPL_TINY_MEMSET=y
 CONFIG_TPM=y
-CONFIG_SPL_TPM=y
+CONFIG_TPM2_TIS_I2C=y
 # CONFIG_EFI_LOADER is not set
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH u-boot v2019.04-aspeed-openbmc 5/5] aspeed: Add board_late_init to write TPM
  2022-05-05 20:28 [PATCH u-boot v2019.04-aspeed-openbmc 0/5] ast2600: Add I2C TPMv2 driver Eddie James
                   ` (3 preceding siblings ...)
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 4/5] configs: ast2600_openbmc_spl_emmc: Enable TPMv2 over I2C Eddie James
@ 2022-05-05 20:28 ` Eddie James
  2022-05-10  2:44   ` Joel Stanley
  4 siblings, 1 reply; 12+ messages in thread
From: Eddie James @ 2022-05-05 20:28 UTC (permalink / raw)
  To: openbmc; +Cc: Eddie James, joel

If there is a TPM in the devicetree, use board_late_init to
extend PCR0 with some invalid digest. The purpose of this is to
prevent later undesired usage of the TPM.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 board/aspeed/evb_ast2600/evb_ast2600.c     | 34 ++++++++++++++++++++++
 configs/ast2600_openbmc_spl_emmc_defconfig |  1 +
 2 files changed, 35 insertions(+)

diff --git a/board/aspeed/evb_ast2600/evb_ast2600.c b/board/aspeed/evb_ast2600/evb_ast2600.c
index 72ecb18c15..e11fc6973d 100644
--- a/board/aspeed/evb_ast2600/evb_ast2600.c
+++ b/board/aspeed/evb_ast2600/evb_ast2600.c
@@ -3,6 +3,11 @@
  * Copyright (C) ASPEED Technology Inc.
  */
 #include <common.h>
+#if defined(CONFIG_TPM_V2)
+#include <dm/uclass.h>
+#include <tpm-common.h>
+#include <tpm-v2.h>
+#endif
 #include <asm/io.h>
 
 #define SCU_BASE	0x1e6e2000
@@ -122,6 +127,35 @@ static void __maybe_unused espi_init(void)
 	writel(reg, ESPI_BASE + 0x000);
 }
 
+__weak int board_late_init(void)
+{
+#if defined(CONFIG_TPM_V2)
+	int rc;
+	struct udevice *dev;
+	unsigned char digest[32] = {
+		0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01,
+		0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+		0xa0, 0xb1, 0xc2, 0xd3, 0xe4, 0xf5, 0x06, 0x17,
+		0x28, 0x39, 0x4a, 0x5b, 0x6c, 0x7d, 0x8e, 0x9f
+	};
+
+	rc = uclass_first_device_err(UCLASS_TPM, &dev);
+	if (rc)
+		return 0;
+
+	rc = tpm_init(dev);
+	if (rc)
+		return 0;
+
+	rc = tpm2_startup(dev, TPM2_SU_CLEAR);
+	if (rc)
+		return 0;
+
+	tpm2_pcr_extend(dev, 0, digest);
+#endif
+	return 0;
+}
+
 int board_early_init_f(void)
 {
 #if 0
diff --git a/configs/ast2600_openbmc_spl_emmc_defconfig b/configs/ast2600_openbmc_spl_emmc_defconfig
index 3bb44280c7..b506bc5e55 100644
--- a/configs/ast2600_openbmc_spl_emmc_defconfig
+++ b/configs/ast2600_openbmc_spl_emmc_defconfig
@@ -39,6 +39,7 @@ CONFIG_SYS_CONSOLE_ENV_OVERWRITE=y
 CONFIG_DISPLAY_BOARDINFO_LATE=y
 CONFIG_ARCH_EARLY_INIT_R=y
 CONFIG_BOARD_EARLY_INIT_F=y
+CONFIG_BOARD_LATE_INIT=y
 CONFIG_SPL_BOARD_INIT=y
 # CONFIG_SPL_LEGACY_IMAGE_SUPPORT is not set
 CONFIG_SPL_SYS_MALLOC_SIMPLE=y
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH u-boot v2019.04-aspeed-openbmc 1/5] i2c: ast_i2c: Remove SCL direct drive mode
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 1/5] i2c: ast_i2c: Remove SCL direct drive mode Eddie James
@ 2022-05-10  2:29   ` Joel Stanley
  0 siblings, 0 replies; 12+ messages in thread
From: Joel Stanley @ 2022-05-10  2:29 UTC (permalink / raw)
  To: Eddie James; +Cc: OpenBMC Maillist

On Thu, 5 May 2022 at 20:28, Eddie James <eajames@linux.ibm.com> wrote:
>
> SCL direct drive mode prevents communication with devices that
> do clock stretching, so disable. The Linux driver doesn't use
> this mode, and the engine can handle clock stretching.
>
> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> ---
>  drivers/i2c/ast_i2c.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/ast_i2c.c b/drivers/i2c/ast_i2c.c
> index bbc32d6cdb..13420ade84 100644
> --- a/drivers/i2c/ast_i2c.c
> +++ b/drivers/i2c/ast_i2c.c
> @@ -73,7 +73,7 @@ static void ast_i2c_init_bus(struct udevice *dev)
>         /* Enable Master Mode. Assuming single-master */
>         writel(I2CD_MASTER_EN
>                | I2CD_M_SDA_LOCK_EN
> -              | I2CD_MULTI_MASTER_DIS | I2CD_M_SCL_DRIVE_EN,
> +              | I2CD_MULTI_MASTER_DIS,

This driver is upstream, so it can be submitted there too.

>                &priv->regs->fcr);
>         /* Enable Interrupts */
>         writel(I2CD_INTR_TX_ACK
> --
> 2.27.0
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH u-boot v2019.04-aspeed-openbmc 2/5] tpm: Add I2C driver for TPMv2 devices
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 2/5] tpm: Add I2C driver for TPMv2 devices Eddie James
@ 2022-05-10  2:42   ` Joel Stanley
  2022-05-11 20:10     ` Eddie James
  0 siblings, 1 reply; 12+ messages in thread
From: Joel Stanley @ 2022-05-10  2:42 UTC (permalink / raw)
  To: Eddie James; +Cc: OpenBMC Maillist

On Thu, 5 May 2022 at 20:28, Eddie James <eajames@linux.ibm.com> wrote:
>
> Add a driver to communicate with TPMv2 chips over I2C, such
> as the NPCT75X.
>
> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> ---
>  drivers/tpm/Kconfig        |   9 +
>  drivers/tpm/Makefile       |   1 +
>  drivers/tpm/tpm2_tis_i2c.c | 593 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 603 insertions(+)
>  create mode 100644 drivers/tpm/tpm2_tis_i2c.c
>
> diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
> index 94629dffd2..6fd98ac057 100644
> --- a/drivers/tpm/Kconfig
> +++ b/drivers/tpm/Kconfig
> @@ -145,6 +145,15 @@ config TPM2_TIS_SPI
>           to the device using the standard TPM Interface Specification (TIS)
>           protocol.
>
> +config TPM2_TIS_I2C
> +       bool "Enable support for TPMv2.x I2C chips"
> +       depends on TPM_V2 && DM_I2C
> +       help
> +         This driver supports TPMv2.x devices connected on the I2C bus.
> +         The usual TPM operations and the 'tpm' command can be used to talk
> +         to the device using the standard TPM Interface Specification (TIS)
> +         protocol.
> +
>  endif # TPM_V2
>
>  endmenu
> diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
> index 94c337b8ed..220f03253c 100644
> --- a/drivers/tpm/Makefile
> +++ b/drivers/tpm/Makefile
> @@ -12,3 +12,4 @@ obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
>
>  obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
>  obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
> +obj-$(CONFIG_TPM2_TIS_I2C) += tpm2_tis_i2c.o
> diff --git a/drivers/tpm/tpm2_tis_i2c.c b/drivers/tpm/tpm2_tis_i2c.c
> new file mode 100644
> index 0000000000..5fab9122e6
> --- /dev/null
> +++ b/drivers/tpm/tpm2_tis_i2c.c
> @@ -0,0 +1,593 @@
> +// SPDX-License-Identifier: GPL-2.0

Add:

Copyright 2022 IBM Corp.

> +
> +#include <common.h>
> +#include <dm.h>
> +#include <fdtdec.h>
> +#include <i2c.h>
> +#include <tpm-v1.h>
> +#include <linux/errno.h>
> +#include <linux/compiler.h>
> +#include <linux/types.h>
> +#include <linux/unaligned/be_byteshift.h>
> +
> +#include "tpm_tis.h"
> +#include "tpm_internal.h"
> +
> +enum i2c_chip_type {
> +       NPCT75X,
> +       UNKNOWN,
> +};
> +
> +/* expected value for DIDVID register */
> +#define TPM2_TIS_I2C_DID_VID_NPCT75X 0x5010FC00L
> +
> +static const char * const chip_name[] = {
> +       [NPCT75X] = "npct75X",
> +       [UNKNOWN] = "unknown/fallback to npct75X",
> +};
> +
> +#define TPM_LOC_SEL            0x00
> +#define        TPM_ACCESS              0x04
> +#define        TPM_STS                 0x18
> +#define        TPM_DATA_FIFO           0x24
> +#define        TPM_DID_VID             0x48
> +
> +/*
> + * tpm2_tis_i2c_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.
> + *
> + * Return -EIO on error, 0 on success.
> + */
> +static int tpm2_tis_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
> +                            size_t len)
> +{
> +       int rc;
> +       int count;
> +
> +       for (count = 0; count < MAX_COUNT; count++) {
> +               rc = dm_i2c_read(dev, addr, buffer, len);
> +               if (rc == 0) {
> +                       debug("read addr[%02x] len[%u] data[%02x %02x %02x %02x%s]\n", addr, len, len > 0 ? buffer[0] : 0, len > 1 ? buffer[1] : 0, len > 2 ? buffer[2] : 0, len > 3 ? buffer[3] : 0, len > 4 ? " ..." : "");
> +                       break;  /* break here to skip sleep */
> +               }
> +               udelay(SLEEP_DURATION_US);
> +       }
> +
> +       /* Take care of 'guard time' */
> +       udelay(SLEEP_DURATION_US);

This will sleep again if the read has reached MAX_COUNT. Could it
return an error straight away? Or is this a TPM spec thing?

Why are you looping several times? Is that a TPM spec thing?

The kernel does this:

do {
 ret = i2c_transfer()
 sleep(GUARD_TIME)
} while (ret < 0 && count++ < GUARD_TIME)

Which makes more sense.

> +       if (rc)
> +               return rc;
> +
> +       return 0;
> +}
> +
> +static int tpm2_tis_i2c_write_generic(struct udevice *dev, u8 addr,
> +                                     const u8 *buffer, size_t len,
> +                                     unsigned int sleep_time_us, u8 max_count)
> +{
> +       int rc = 0;
> +       int count;
> +
> +       for (count = 0; count < max_count; count++) {
> +               rc = dm_i2c_write(dev, addr, buffer, len);
> +               if (rc == 0) {
> +                       debug("write addr[%02x] len[%u] data[%02x %02x %02x %02x%s]\n", addr, len, len > 0 ? buffer[0] : 0, len > 1 ? buffer[1] : 0, len > 2 ? buffer[2] : 0, len > 3 ? buffer[3] : 0, len > 4 ? " ..." : "");
> +                       break;  /* Success, break to skip sleep */
> +               }
> +               udelay(sleep_time_us);
> +       }
> +
> +       /* take care of 'guard time' */
> +       udelay(sleep_time_us);
> +       if (rc)
> +               return rc;
> +
> +       return 0;
> +}
> +
> +/*
> + * tpm2_tis_i2c_write() - write to TPM register
> + * @addr: register address to write to
> + * @buffer: containing data to be written
> + * @len: number of bytes to write
> + *
> + * Write len bytes from provided buffer to TPM register (little
> + * endian format, i.e. buffer[0] is written as first byte).
> + *
> + * NOTE: TPM is big-endian for multi-byte values. Multi-byte
> + * values have to be swapped.
> + *
> + * NOTE: use this function instead of the tpm2_tis_i2c_write_generic function.

Why the extra indirection?

Actually, I see why. This code is a copy of
drivers/tpm/tpm_tis_infineon.c with some things changed. Can you go
through the patch and remove things that don't need to be in this
driver?

The kernel driver is a good reference for things that might be
required for the spec, vs coding decisions made by the infineon driver
author.


> + *
> + * Return -EIO on error, 0 on success
> + */
> +static int tpm2_tis_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
> +                             size_t len)
> +{
> +       return tpm2_tis_i2c_write_generic(dev, addr, buffer, len,
> +                                         SLEEP_DURATION_US, MAX_COUNT);
> +}
> +
> +/*
> + * This function is needed especially for the cleanup situation after
> + * sending TPM_READY
> + */
> +static int tpm2_tis_i2c_write_long(struct udevice *dev, u8 addr, u8 *buffer,
> +                                  size_t len)
> +{
> +       return tpm2_tis_i2c_write_generic(dev, addr, buffer, len,
> +                                         SLEEP_DURATION_LONG_US,
> +                                         MAX_COUNT_LONG);
> +}
> +
> +static int tpm2_tis_i2c_check_locality(struct udevice *dev, int loc)
> +{
> +       const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +       u8 buf;
> +       int rc;
> +
> +       buf = loc;
> +       rc = tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1);
> +       if (rc < 0)
> +               return rc;
> +
> +       rc = tpm2_tis_i2c_read(dev, TPM_ACCESS, &buf, 1);
> +       if (rc < 0)
> +               return rc;
> +
> +       if ((buf & mask) == mask) {
> +               chip->locality = loc;
> +               return loc;
> +       }
> +
> +       return -ENOENT;
> +}
> +
> +static void tpm2_tis_i2c_release_locality(struct udevice *dev, int loc,
> +                                        int force)
> +{
> +       const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
> +       u8 buf;
> +
> +       buf = loc;
> +       if (tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1) < 0)
> +               return;
> +
> +       if (tpm2_tis_i2c_read(dev, TPM_ACCESS, &buf, 1) < 0)
> +               return;
> +
> +       if (force || (buf & mask) == mask) {
> +               buf = TPM_ACCESS_ACTIVE_LOCALITY;
> +               tpm2_tis_i2c_write(dev, TPM_ACCESS, &buf, 1);
> +       }
> +}
> +
> +static int tpm2_tis_i2c_request_locality(struct udevice *dev, int loc)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +       unsigned long start, stop;
> +       u8 buf = 0;
> +       int rc;
> +
> +       rc = tpm2_tis_i2c_check_locality(dev, loc);
> +       if (rc >= 0) {
> +               debug("%s: Already have locality\n", __func__);
> +               return loc;  /* We already have the locality */
> +       } else if (rc != -ENOENT) {
> +               debug("%s: Failed to get locality: %d\n", __func__, rc);
> +               return rc;
> +       }
> +
> +       buf = loc;
> +       rc = tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1);
> +       if (rc) {
> +               debug("%s: Failed to write to TPM: %d\n", __func__, rc);
> +               return rc;
> +       }
> +
> +       buf = TPM_ACCESS_REQUEST_USE;
> +       rc = tpm2_tis_i2c_write(dev, TPM_ACCESS, &buf, 1);
> +       if (rc) {
> +               debug("%s: Failed to write to TPM: %d\n", __func__, rc);
> +               return rc;
> +       }
> +
> +       /* Wait for burstcount */
> +       start = get_timer(0);
> +       stop = chip->timeout_a;
> +       do {
> +               rc = tpm2_tis_i2c_check_locality(dev, loc);
> +               if (rc >= 0) {
> +                       debug("%s: Have locality\n", __func__);
> +                       return loc;
> +               } else if (rc != -ENOENT) {
> +                       debug("%s: Failed to get locality: %d\n", __func__, rc);
> +                       return rc;
> +               }
> +               mdelay(TPM_TIMEOUT_MS);
> +       } while (get_timer(start) < stop);
> +       debug("%s: Timeout getting locality: %d\n", __func__, rc);
> +
> +       return rc;
> +}
> +
> +static u8 tpm2_tis_i2c_status(struct udevice *dev)
> +{
> +       /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
> +       u8 buf;
> +
> +       if (tpm2_tis_i2c_read(dev, TPM_STS, &buf, 1) < 0)
> +               return 0;
> +       else
> +               return buf;
> +}
> +
> +static int tpm2_tis_i2c_ready(struct udevice *dev)
> +{
> +       int rc;
> +
> +       /* This causes the current command to be aborted */
> +       u8 buf = TPM_STS_COMMAND_READY;
> +
> +       debug("%s\n", __func__);
> +       rc = tpm2_tis_i2c_write_long(dev, TPM_STS, &buf, 1);
> +       if (rc)
> +               debug("%s: rc=%d\n", __func__, rc);
> +
> +       return rc;
> +}
> +
> +static ssize_t tpm2_tis_i2c_get_burstcount(struct udevice *dev)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +       unsigned long start, stop;
> +       ssize_t burstcnt;
> +       u8 addr, buf[3];
> +
> +       /* Wait for burstcount */
> +       /* XXX: Which timeout value? Spec has 2 answers (c & d) */
> +       start = get_timer(0);
> +       stop = chip->timeout_d;
> +       do {
> +               /* Note: STS is little endian */
> +               addr = TPM_STS + 1;
> +               if (tpm2_tis_i2c_read(dev, addr, buf, 2) < 0)
> +                       burstcnt = 0;
> +               else
> +                       burstcnt = (buf[1] << 8) + buf[0];
> +
> +               if (burstcnt)
> +                       return burstcnt;
> +               mdelay(TPM_TIMEOUT_MS);
> +       } while (get_timer(start) < stop);
> +
> +       return -EBUSY;
> +}
> +
> +static int tpm2_tis_i2c_wait_for_stat(struct udevice *dev, u8 mask,
> +                                     unsigned long timeout, int *status)
> +{
> +       unsigned long start, stop;
> +
> +       /* Check current status */
> +       *status = tpm2_tis_i2c_status(dev);
> +       if ((*status & mask) == mask)
> +               return 0;
> +
> +       start = get_timer(0);
> +       stop = timeout;
> +       do {
> +               mdelay(TPM_TIMEOUT_MS);
> +               *status = tpm2_tis_i2c_status(dev);
> +               if ((*status & mask) == mask)
> +                       return 0;
> +       } while (get_timer(start) < stop);
> +
> +       return -ETIMEDOUT;
> +}
> +
> +static int tpm2_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
> +{
> +       size_t size = 0;
> +       ssize_t burstcnt;
> +       int rc;
> +
> +       while (size < count) {
> +               burstcnt = tpm2_tis_i2c_get_burstcount(dev);
> +
> +               /* burstcount < 0 -> tpm is busy */
> +               if (burstcnt < 0)
> +                       return burstcnt;
> +
> +               /* Limit received data to max left */
> +               if (burstcnt > (count - size))
> +                       burstcnt = count - size;
> +
> +               rc = tpm2_tis_i2c_read(dev, TPM_DATA_FIFO,
> +                                      &(buf[size]), burstcnt);
> +               if (rc == 0)
> +                       size += burstcnt;
> +       }
> +
> +       return size;
> +}
> +
> +static int tpm2_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +       int size = 0;
> +       int status;
> +       unsigned int expected;
> +       int rc;
> +
> +       status = tpm2_tis_i2c_status(dev);
> +       if (status == TPM_STS_COMMAND_READY)
> +               return -EINTR;
> +       if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) !=
> +           (TPM_STS_DATA_AVAIL | TPM_STS_VALID))
> +               return -EAGAIN;
> +
> +       debug("...got it;\n");
> +
> +       /* Read first 10 bytes, including tag, paramsize, and result */
> +       size = tpm2_tis_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
> +       if (size < TPM_HEADER_SIZE) {
> +               debug("Unable to read header\n");
> +               return size < 0 ? size : -EIO;
> +       }
> +
> +       expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
> +       if ((size_t)expected > count || (size_t)expected < TPM_HEADER_SIZE) {
> +               debug("Error size=%x, expected=%x, count=%x\n", size, expected,
> +                     count);
> +               return -ENOSPC;
> +       }
> +
> +       size += tpm2_tis_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
> +                                      expected - TPM_HEADER_SIZE);
> +       if (size < expected) {
> +               debug("Unable to read remainder of result\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c,
> +                                       &status);
> +       if (rc)
> +               return rc;
> +       if (status & TPM_STS_DATA_AVAIL) {  /* Retry? */
> +               debug("Error left over data\n");
> +               return -EIO;
> +       }
> +
> +       return size;
> +}
> +
> +static int tpm2_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +       int rc, status;
> +       size_t burstcnt;
> +       size_t count = 0;
> +       int retry = 0;
> +       u8 sts = TPM_STS_GO;
> +
> +       debug("%s: len=%d\n", __func__, len);
> +       if (len > TPM_DEV_BUFSIZE)
> +               return -E2BIG;  /* Command is too long for our tpm, sorry */
> +
> +       if (tpm2_tis_i2c_request_locality(dev, 0) < 0)
> +               return -EBUSY;
> +
> +       status = tpm2_tis_i2c_status(dev);
> +       if ((status & TPM_STS_COMMAND_READY) == 0) {
> +               rc = tpm2_tis_i2c_ready(dev);
> +               if (rc)
> +                       return rc;
> +               rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
> +                                               chip->timeout_b, &status);
> +               if (rc)
> +                       return rc;
> +       }
> +
> +       burstcnt = tpm2_tis_i2c_get_burstcount(dev);
> +
> +       /* burstcount < 0 -> tpm is busy */
> +       if (burstcnt < 0)
> +               return burstcnt;
> +
> +       while (count < len) {
> +               udelay(300);
> +               if (burstcnt > len - count)
> +                       burstcnt = len - count;
> +
> +#ifdef CONFIG_TPM2_TIS_I2C_BURST_LIMITATION
> +               if (retry && burstcnt > CONFIG_TPM2_TIS_I2C_BURST_LIMITATION_LEN)
> +                       burstcnt = CONFIG_TPM2_TIS_I2C_BURST_LIMITATION_LEN;
> +#endif /* CONFIG_TPM2_TIS_I2C_BURST_LIMITATION */
> +
> +               rc = tpm2_tis_i2c_write(dev, TPM_DATA_FIFO,
> +                                       &(buf[count]), burstcnt);
> +               if (rc == 0)
> +                       count += burstcnt;
> +               else {
> +                       debug("%s: error\n", __func__);
> +                       if (retry++ > 10)
> +                               return -EIO;
> +                       rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_VALID,
> +                                                       chip->timeout_c,
> +                                                       &status);
> +                       if (rc)
> +                               return rc;
> +
> +                       if ((status & TPM_STS_DATA_EXPECT) == 0)
> +                               return -EIO;
> +               }
> +       }
> +
> +       /* Go and do it */
> +       rc = tpm2_tis_i2c_write(dev, TPM_STS, &sts, 1);
> +       if (rc < 0)
> +               return rc;
> +       debug("%s: done, rc=%d\n", __func__, rc);
> +
> +       return len;
> +}
> +
> +static int tpm2_tis_i2c_cleanup(struct udevice *dev)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +
> +       tpm2_tis_i2c_ready(dev);
> +       /*
> +        * The TPM needs some time to clean up here,
> +        * so we sleep rather than keeping the bus busy
> +        */
> +       mdelay(2);
> +       tpm2_tis_i2c_release_locality(dev, chip->locality, 0);
> +
> +       return 0;
> +}
> +
> +static int tpm2_tis_i2c_init(struct udevice *dev)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +       u32 vendor;
> +       u32 expected_did_vid;
> +       int rc;
> +
> +       chip->is_open = 1;
> +
> +       /* Default timeouts - these could move to the device tree */
> +       chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
> +       chip->timeout_b = TIS_LONG_TIMEOUT_MS;
> +       chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
> +       chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
> +
> +       rc = tpm2_tis_i2c_request_locality(dev, 0);
> +       if (rc < 0)
> +               return rc;
> +
> +       /* Read four bytes from DID_VID register */
> +       if (tpm2_tis_i2c_read(dev, TPM_DID_VID, (uchar *)&vendor, 4) < 0) {
> +               tpm2_tis_i2c_release_locality(dev, 0, 1);
> +               return -EIO;
> +       }
> +
> +       if (chip->chip_type == NPCT75X) {
> +               vendor = be32_to_cpu(vendor);
> +               expected_did_vid = TPM2_TIS_I2C_DID_VID_NPCT75X;
> +       }
> +
> +       if (chip->chip_type != UNKNOWN && vendor != expected_did_vid) {
> +               pr_err("Vendor id did not match! ID was %08x\n", vendor);
> +               return -ENODEV;
> +       }
> +
> +       chip->vend_dev = vendor;
> +       debug("2.0 TPM (chip type %s device-id 0x%X)\n",
> +             chip_name[chip->chip_type], vendor >> 16);
> +
> +       /*
> +        * A timeout query to TPM can be placed here.
> +        * Standard timeout values are used so far
> +        */
> +
> +       return 0;
> +}
> +
> +static int tpm2_tis_i2c_open(struct udevice *dev)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +       int rc;
> +
> +       debug("%s: start\n", __func__);
> +       if (chip->is_open)
> +               return -EBUSY;
> +       rc = tpm2_tis_i2c_init(dev);
> +       if (rc < 0)
> +               chip->is_open = 0;
> +
> +       return rc;
> +}
> +
> +static int tpm2_tis_i2c_close(struct udevice *dev)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +
> +       if (chip->is_open) {
> +               tpm2_tis_i2c_release_locality(dev, chip->locality, 1);
> +               chip->is_open = 0;
> +               chip->vend_dev = 0;
> +       }
> +
> +       return 0;
> +}
> +
> +static int tpm2_tis_get_desc(struct udevice *dev, char *buf, int size)
> +{
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +
> +       if (size < 50)
> +               return -ENOSPC;
> +
> +       return snprintf(buf, size, "2.0 TPM (%s, chip type %s device-id 0x%x)",
> +                       chip->is_open ? "open" : "closed",
> +                       chip_name[chip->chip_type],
> +                       chip->vend_dev >> 16);
> +}
> +
> +static int tpm2_tis_i2c_probe(struct udevice *dev)
> +{
> +       struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
> +       struct tpm_chip *chip = dev_get_priv(dev);
> +
> +       chip->chip_type = dev_get_driver_data(dev);
> +       chip->locality = 0;
> +       chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
> +       chip->timeout_b = TIS_LONG_TIMEOUT_MS;
> +       chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
> +       chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
> +
> +       /* TODO: These need to be checked and tuned */
> +       uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
> +       uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
> +       uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
> +       uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
> +       uc_priv->pcr_count = 24;
> +       uc_priv->pcr_select_min = 3;
> +       uc_priv->version = TPM_V2;
> +
> +       return 0;
> +}
> +
> +static const struct tpm_ops tpm2_tis_i2c_ops = {
> +       .open           = tpm2_tis_i2c_open,
> +       .close          = tpm2_tis_i2c_close,
> +       .get_desc       = tpm2_tis_get_desc,
> +       .send           = tpm2_tis_i2c_send,
> +       .recv           = tpm2_tis_i2c_recv,
> +       .cleanup        = tpm2_tis_i2c_cleanup,
> +};
> +
> +static const struct udevice_id tpm2_tis_i2c_ids[] = {
> +       { .compatible = "nuvoton,npct75x", .data = NPCT75X },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(tpm2_tis_i2c) = {
> +       .name   = "tpm2_tis_i2c",
> +       .id     = UCLASS_TPM,
> +       .of_match = tpm2_tis_i2c_ids,
> +       .ops    = &tpm2_tis_i2c_ops,
> +       .probe  = tpm2_tis_i2c_probe,
> +       .priv_auto_alloc_size = sizeof(struct tpm_chip),
> +};
> --
> 2.27.0
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH u-boot v2019.04-aspeed-openbmc 5/5] aspeed: Add board_late_init to write TPM
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 5/5] aspeed: Add board_late_init to write TPM Eddie James
@ 2022-05-10  2:44   ` Joel Stanley
  2022-05-11 20:13     ` Eddie James
  0 siblings, 1 reply; 12+ messages in thread
From: Joel Stanley @ 2022-05-10  2:44 UTC (permalink / raw)
  To: Eddie James; +Cc: OpenBMC Maillist

On Thu, 5 May 2022 at 20:28, Eddie James <eajames@linux.ibm.com> wrote:
>
> If there is a TPM in the devicetree, use board_late_init to
> extend PCR0 with some invalid digest. The purpose of this is to
> prevent later undesired usage of the TPM.
>
> Signed-off-by: Eddie James <eajames@linux.ibm.com>

I think we will need a board_late_init for our platform, as others may
use the TPM and not want to poison it.

> ---
>  board/aspeed/evb_ast2600/evb_ast2600.c     | 34 ++++++++++++++++++++++
>  configs/ast2600_openbmc_spl_emmc_defconfig |  1 +
>  2 files changed, 35 insertions(+)
>
> diff --git a/board/aspeed/evb_ast2600/evb_ast2600.c b/board/aspeed/evb_ast2600/evb_ast2600.c
> index 72ecb18c15..e11fc6973d 100644
> --- a/board/aspeed/evb_ast2600/evb_ast2600.c
> +++ b/board/aspeed/evb_ast2600/evb_ast2600.c
> @@ -3,6 +3,11 @@
>   * Copyright (C) ASPEED Technology Inc.
>   */
>  #include <common.h>
> +#if defined(CONFIG_TPM_V2)
> +#include <dm/uclass.h>
> +#include <tpm-common.h>
> +#include <tpm-v2.h>
> +#endif
>  #include <asm/io.h>
>
>  #define SCU_BASE       0x1e6e2000
> @@ -122,6 +127,35 @@ static void __maybe_unused espi_init(void)
>         writel(reg, ESPI_BASE + 0x000);
>  }
>
> +__weak int board_late_init(void)
> +{
> +#if defined(CONFIG_TPM_V2)
> +       int rc;
> +       struct udevice *dev;
> +       unsigned char digest[32] = {
> +               0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01,
> +               0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
> +               0xa0, 0xb1, 0xc2, 0xd3, 0xe4, 0xf5, 0x06, 0x17,
> +               0x28, 0x39, 0x4a, 0x5b, 0x6c, 0x7d, 0x8e, 0x9f
> +       };

Add a comment for how this was created.

> +
> +       rc = uclass_first_device_err(UCLASS_TPM, &dev);
> +       if (rc)
> +               return 0;
> +
> +       rc = tpm_init(dev);
> +       if (rc)
> +               return 0;
> +
> +       rc = tpm2_startup(dev, TPM2_SU_CLEAR);
> +       if (rc)
> +               return 0;
> +
> +       tpm2_pcr_extend(dev, 0, digest);
> +#endif
> +       return 0;
> +}
> +
>  int board_early_init_f(void)
>  {
>  #if 0
> diff --git a/configs/ast2600_openbmc_spl_emmc_defconfig b/configs/ast2600_openbmc_spl_emmc_defconfig
> index 3bb44280c7..b506bc5e55 100644
> --- a/configs/ast2600_openbmc_spl_emmc_defconfig
> +++ b/configs/ast2600_openbmc_spl_emmc_defconfig
> @@ -39,6 +39,7 @@ CONFIG_SYS_CONSOLE_ENV_OVERWRITE=y
>  CONFIG_DISPLAY_BOARDINFO_LATE=y
>  CONFIG_ARCH_EARLY_INIT_R=y
>  CONFIG_BOARD_EARLY_INIT_F=y
> +CONFIG_BOARD_LATE_INIT=y
>  CONFIG_SPL_BOARD_INIT=y
>  # CONFIG_SPL_LEGACY_IMAGE_SUPPORT is not set
>  CONFIG_SPL_SYS_MALLOC_SIMPLE=y
> --
> 2.27.0
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH u-boot v2019.04-aspeed-openbmc 3/5] arm: dts: ast2600-rainier: Add NPCT75X TPM
  2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 3/5] arm: dts: ast2600-rainier: Add NPCT75X TPM Eddie James
@ 2022-05-10  2:44   ` Joel Stanley
  0 siblings, 0 replies; 12+ messages in thread
From: Joel Stanley @ 2022-05-10  2:44 UTC (permalink / raw)
  To: Eddie James; +Cc: OpenBMC Maillist

On Thu, 5 May 2022 at 20:28, Eddie James <eajames@linux.ibm.com> wrote:
>
> Add the TPM device on I2C bus 12.

What happens when a board doesn't have a TPM attached to this bus?

>
> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> ---
>  arch/arm/dts/ast2600-rainier.dts | 12 +++++++-----
>  1 file changed, 7 insertions(+), 5 deletions(-)
>
> diff --git a/arch/arm/dts/ast2600-rainier.dts b/arch/arm/dts/ast2600-rainier.dts
> index 8a44fd8a0a..aa91b12ed3 100755
> --- a/arch/arm/dts/ast2600-rainier.dts
> +++ b/arch/arm/dts/ast2600-rainier.dts
> @@ -106,12 +106,14 @@
>
>  &gpio0 {
>         u-boot,dm-pre-reloc;
> +};
> +
> +&i2c12 {
> +       status = "okay";
>
> -       tpm_reset {
> -               u-boot,dm-pre-reloc;
> -               gpio-hog;
> -               output-high;
> -               gpios = <ASPEED_GPIO(R, 0) GPIO_ACTIVE_HIGH>;
> +       tpm@2e {
> +               compatible = "nuvoton,npct75x";
> +               reg = <0x2e>;
>         };
>  };
>
> --
> 2.27.0
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH u-boot v2019.04-aspeed-openbmc 2/5] tpm: Add I2C driver for TPMv2 devices
  2022-05-10  2:42   ` Joel Stanley
@ 2022-05-11 20:10     ` Eddie James
  0 siblings, 0 replies; 12+ messages in thread
From: Eddie James @ 2022-05-11 20:10 UTC (permalink / raw)
  To: Joel Stanley; +Cc: OpenBMC Maillist


On 5/9/22 21:42, Joel Stanley wrote:
> On Thu, 5 May 2022 at 20:28, Eddie James <eajames@linux.ibm.com> wrote:
>> Add a driver to communicate with TPMv2 chips over I2C, such
>> as the NPCT75X.
>>
>> Signed-off-by: Eddie James <eajames@linux.ibm.com>
>> ---
>>   drivers/tpm/Kconfig        |   9 +
>>   drivers/tpm/Makefile       |   1 +
>>   drivers/tpm/tpm2_tis_i2c.c | 593 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 603 insertions(+)
>>   create mode 100644 drivers/tpm/tpm2_tis_i2c.c
>>
>> diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
>> index 94629dffd2..6fd98ac057 100644
>> --- a/drivers/tpm/Kconfig
>> +++ b/drivers/tpm/Kconfig
>> @@ -145,6 +145,15 @@ config TPM2_TIS_SPI
>>            to the device using the standard TPM Interface Specification (TIS)
>>            protocol.
>>
>> +config TPM2_TIS_I2C
>> +       bool "Enable support for TPMv2.x I2C chips"
>> +       depends on TPM_V2 && DM_I2C
>> +       help
>> +         This driver supports TPMv2.x devices connected on the I2C bus.
>> +         The usual TPM operations and the 'tpm' command can be used to talk
>> +         to the device using the standard TPM Interface Specification (TIS)
>> +         protocol.
>> +
>>   endif # TPM_V2
>>
>>   endmenu
>> diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
>> index 94c337b8ed..220f03253c 100644
>> --- a/drivers/tpm/Makefile
>> +++ b/drivers/tpm/Makefile
>> @@ -12,3 +12,4 @@ obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
>>
>>   obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
>>   obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
>> +obj-$(CONFIG_TPM2_TIS_I2C) += tpm2_tis_i2c.o
>> diff --git a/drivers/tpm/tpm2_tis_i2c.c b/drivers/tpm/tpm2_tis_i2c.c
>> new file mode 100644
>> index 0000000000..5fab9122e6
>> --- /dev/null
>> +++ b/drivers/tpm/tpm2_tis_i2c.c
>> @@ -0,0 +1,593 @@
>> +// SPDX-License-Identifier: GPL-2.0
> Add:
>
> Copyright 2022 IBM Corp.


Ack.


>
>> +
>> +#include <common.h>
>> +#include <dm.h>
>> +#include <fdtdec.h>
>> +#include <i2c.h>
>> +#include <tpm-v1.h>
>> +#include <linux/errno.h>
>> +#include <linux/compiler.h>
>> +#include <linux/types.h>
>> +#include <linux/unaligned/be_byteshift.h>
>> +
>> +#include "tpm_tis.h"
>> +#include "tpm_internal.h"
>> +
>> +enum i2c_chip_type {
>> +       NPCT75X,
>> +       UNKNOWN,
>> +};
>> +
>> +/* expected value for DIDVID register */
>> +#define TPM2_TIS_I2C_DID_VID_NPCT75X 0x5010FC00L
>> +
>> +static const char * const chip_name[] = {
>> +       [NPCT75X] = "npct75X",
>> +       [UNKNOWN] = "unknown/fallback to npct75X",
>> +};
>> +
>> +#define TPM_LOC_SEL            0x00
>> +#define        TPM_ACCESS              0x04
>> +#define        TPM_STS                 0x18
>> +#define        TPM_DATA_FIFO           0x24
>> +#define        TPM_DID_VID             0x48
>> +
>> +/*
>> + * tpm2_tis_i2c_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.
>> + *
>> + * Return -EIO on error, 0 on success.
>> + */
>> +static int tpm2_tis_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
>> +                            size_t len)
>> +{
>> +       int rc;
>> +       int count;
>> +
>> +       for (count = 0; count < MAX_COUNT; count++) {
>> +               rc = dm_i2c_read(dev, addr, buffer, len);
>> +               if (rc == 0) {
>> +                       debug("read addr[%02x] len[%u] data[%02x %02x %02x %02x%s]\n", addr, len, len > 0 ? buffer[0] : 0, len > 1 ? buffer[1] : 0, len > 2 ? buffer[2] : 0, len > 3 ? buffer[3] : 0, len > 4 ? " ..." : "");
>> +                       break;  /* break here to skip sleep */
>> +               }
>> +               udelay(SLEEP_DURATION_US);
>> +       }
>> +
>> +       /* Take care of 'guard time' */
>> +       udelay(SLEEP_DURATION_US);
> This will sleep again if the read has reached MAX_COUNT. Could it
> return an error straight away? Or is this a TPM spec thing?
>
> Why are you looping several times? Is that a TPM spec thing?
>
> The kernel does this:
>
> do {
>   ret = i2c_transfer()
>   sleep(GUARD_TIME)
> } while (ret < 0 && count++ < GUARD_TIME)
>
> Which makes more sense.


Yea, I'll switch to that.


>
>> +       if (rc)
>> +               return rc;
>> +
>> +       return 0;
>> +}
>> +
>> +static int tpm2_tis_i2c_write_generic(struct udevice *dev, u8 addr,
>> +                                     const u8 *buffer, size_t len,
>> +                                     unsigned int sleep_time_us, u8 max_count)
>> +{
>> +       int rc = 0;
>> +       int count;
>> +
>> +       for (count = 0; count < max_count; count++) {
>> +               rc = dm_i2c_write(dev, addr, buffer, len);
>> +               if (rc == 0) {
>> +                       debug("write addr[%02x] len[%u] data[%02x %02x %02x %02x%s]\n", addr, len, len > 0 ? buffer[0] : 0, len > 1 ? buffer[1] : 0, len > 2 ? buffer[2] : 0, len > 3 ? buffer[3] : 0, len > 4 ? " ..." : "");
>> +                       break;  /* Success, break to skip sleep */
>> +               }
>> +               udelay(sleep_time_us);
>> +       }
>> +
>> +       /* take care of 'guard time' */
>> +       udelay(sleep_time_us);
>> +       if (rc)
>> +               return rc;
>> +
>> +       return 0;
>> +}
>> +
>> +/*
>> + * tpm2_tis_i2c_write() - write to TPM register
>> + * @addr: register address to write to
>> + * @buffer: containing data to be written
>> + * @len: number of bytes to write
>> + *
>> + * Write len bytes from provided buffer to TPM register (little
>> + * endian format, i.e. buffer[0] is written as first byte).
>> + *
>> + * NOTE: TPM is big-endian for multi-byte values. Multi-byte
>> + * values have to be swapped.
>> + *
>> + * NOTE: use this function instead of the tpm2_tis_i2c_write_generic function.
> Why the extra indirection?
>
> Actually, I see why. This code is a copy of
> drivers/tpm/tpm_tis_infineon.c with some things changed. Can you go
> through the patch and remove things that don't need to be in this
> driver?
>
> The kernel driver is a good reference for things that might be
> required for the spec, vs coding decisions made by the infineon driver
> author.


Sure, I'll clean it up.

Thanks for the review!


>
>
>> + *
>> + * Return -EIO on error, 0 on success
>> + */
>> +static int tpm2_tis_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
>> +                             size_t len)
>> +{
>> +       return tpm2_tis_i2c_write_generic(dev, addr, buffer, len,
>> +                                         SLEEP_DURATION_US, MAX_COUNT);
>> +}
>> +
>> +/*
>> + * This function is needed especially for the cleanup situation after
>> + * sending TPM_READY
>> + */
>> +static int tpm2_tis_i2c_write_long(struct udevice *dev, u8 addr, u8 *buffer,
>> +                                  size_t len)
>> +{
>> +       return tpm2_tis_i2c_write_generic(dev, addr, buffer, len,
>> +                                         SLEEP_DURATION_LONG_US,
>> +                                         MAX_COUNT_LONG);
>> +}
>> +
>> +static int tpm2_tis_i2c_check_locality(struct udevice *dev, int loc)
>> +{
>> +       const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +       u8 buf;
>> +       int rc;
>> +
>> +       buf = loc;
>> +       rc = tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1);
>> +       if (rc < 0)
>> +               return rc;
>> +
>> +       rc = tpm2_tis_i2c_read(dev, TPM_ACCESS, &buf, 1);
>> +       if (rc < 0)
>> +               return rc;
>> +
>> +       if ((buf & mask) == mask) {
>> +               chip->locality = loc;
>> +               return loc;
>> +       }
>> +
>> +       return -ENOENT;
>> +}
>> +
>> +static void tpm2_tis_i2c_release_locality(struct udevice *dev, int loc,
>> +                                        int force)
>> +{
>> +       const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
>> +       u8 buf;
>> +
>> +       buf = loc;
>> +       if (tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1) < 0)
>> +               return;
>> +
>> +       if (tpm2_tis_i2c_read(dev, TPM_ACCESS, &buf, 1) < 0)
>> +               return;
>> +
>> +       if (force || (buf & mask) == mask) {
>> +               buf = TPM_ACCESS_ACTIVE_LOCALITY;
>> +               tpm2_tis_i2c_write(dev, TPM_ACCESS, &buf, 1);
>> +       }
>> +}
>> +
>> +static int tpm2_tis_i2c_request_locality(struct udevice *dev, int loc)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +       unsigned long start, stop;
>> +       u8 buf = 0;
>> +       int rc;
>> +
>> +       rc = tpm2_tis_i2c_check_locality(dev, loc);
>> +       if (rc >= 0) {
>> +               debug("%s: Already have locality\n", __func__);
>> +               return loc;  /* We already have the locality */
>> +       } else if (rc != -ENOENT) {
>> +               debug("%s: Failed to get locality: %d\n", __func__, rc);
>> +               return rc;
>> +       }
>> +
>> +       buf = loc;
>> +       rc = tpm2_tis_i2c_write(dev, TPM_LOC_SEL, &buf, 1);
>> +       if (rc) {
>> +               debug("%s: Failed to write to TPM: %d\n", __func__, rc);
>> +               return rc;
>> +       }
>> +
>> +       buf = TPM_ACCESS_REQUEST_USE;
>> +       rc = tpm2_tis_i2c_write(dev, TPM_ACCESS, &buf, 1);
>> +       if (rc) {
>> +               debug("%s: Failed to write to TPM: %d\n", __func__, rc);
>> +               return rc;
>> +       }
>> +
>> +       /* Wait for burstcount */
>> +       start = get_timer(0);
>> +       stop = chip->timeout_a;
>> +       do {
>> +               rc = tpm2_tis_i2c_check_locality(dev, loc);
>> +               if (rc >= 0) {
>> +                       debug("%s: Have locality\n", __func__);
>> +                       return loc;
>> +               } else if (rc != -ENOENT) {
>> +                       debug("%s: Failed to get locality: %d\n", __func__, rc);
>> +                       return rc;
>> +               }
>> +               mdelay(TPM_TIMEOUT_MS);
>> +       } while (get_timer(start) < stop);
>> +       debug("%s: Timeout getting locality: %d\n", __func__, rc);
>> +
>> +       return rc;
>> +}
>> +
>> +static u8 tpm2_tis_i2c_status(struct udevice *dev)
>> +{
>> +       /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
>> +       u8 buf;
>> +
>> +       if (tpm2_tis_i2c_read(dev, TPM_STS, &buf, 1) < 0)
>> +               return 0;
>> +       else
>> +               return buf;
>> +}
>> +
>> +static int tpm2_tis_i2c_ready(struct udevice *dev)
>> +{
>> +       int rc;
>> +
>> +       /* This causes the current command to be aborted */
>> +       u8 buf = TPM_STS_COMMAND_READY;
>> +
>> +       debug("%s\n", __func__);
>> +       rc = tpm2_tis_i2c_write_long(dev, TPM_STS, &buf, 1);
>> +       if (rc)
>> +               debug("%s: rc=%d\n", __func__, rc);
>> +
>> +       return rc;
>> +}
>> +
>> +static ssize_t tpm2_tis_i2c_get_burstcount(struct udevice *dev)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +       unsigned long start, stop;
>> +       ssize_t burstcnt;
>> +       u8 addr, buf[3];
>> +
>> +       /* Wait for burstcount */
>> +       /* XXX: Which timeout value? Spec has 2 answers (c & d) */
>> +       start = get_timer(0);
>> +       stop = chip->timeout_d;
>> +       do {
>> +               /* Note: STS is little endian */
>> +               addr = TPM_STS + 1;
>> +               if (tpm2_tis_i2c_read(dev, addr, buf, 2) < 0)
>> +                       burstcnt = 0;
>> +               else
>> +                       burstcnt = (buf[1] << 8) + buf[0];
>> +
>> +               if (burstcnt)
>> +                       return burstcnt;
>> +               mdelay(TPM_TIMEOUT_MS);
>> +       } while (get_timer(start) < stop);
>> +
>> +       return -EBUSY;
>> +}
>> +
>> +static int tpm2_tis_i2c_wait_for_stat(struct udevice *dev, u8 mask,
>> +                                     unsigned long timeout, int *status)
>> +{
>> +       unsigned long start, stop;
>> +
>> +       /* Check current status */
>> +       *status = tpm2_tis_i2c_status(dev);
>> +       if ((*status & mask) == mask)
>> +               return 0;
>> +
>> +       start = get_timer(0);
>> +       stop = timeout;
>> +       do {
>> +               mdelay(TPM_TIMEOUT_MS);
>> +               *status = tpm2_tis_i2c_status(dev);
>> +               if ((*status & mask) == mask)
>> +                       return 0;
>> +       } while (get_timer(start) < stop);
>> +
>> +       return -ETIMEDOUT;
>> +}
>> +
>> +static int tpm2_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
>> +{
>> +       size_t size = 0;
>> +       ssize_t burstcnt;
>> +       int rc;
>> +
>> +       while (size < count) {
>> +               burstcnt = tpm2_tis_i2c_get_burstcount(dev);
>> +
>> +               /* burstcount < 0 -> tpm is busy */
>> +               if (burstcnt < 0)
>> +                       return burstcnt;
>> +
>> +               /* Limit received data to max left */
>> +               if (burstcnt > (count - size))
>> +                       burstcnt = count - size;
>> +
>> +               rc = tpm2_tis_i2c_read(dev, TPM_DATA_FIFO,
>> +                                      &(buf[size]), burstcnt);
>> +               if (rc == 0)
>> +                       size += burstcnt;
>> +       }
>> +
>> +       return size;
>> +}
>> +
>> +static int tpm2_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +       int size = 0;
>> +       int status;
>> +       unsigned int expected;
>> +       int rc;
>> +
>> +       status = tpm2_tis_i2c_status(dev);
>> +       if (status == TPM_STS_COMMAND_READY)
>> +               return -EINTR;
>> +       if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) !=
>> +           (TPM_STS_DATA_AVAIL | TPM_STS_VALID))
>> +               return -EAGAIN;
>> +
>> +       debug("...got it;\n");
>> +
>> +       /* Read first 10 bytes, including tag, paramsize, and result */
>> +       size = tpm2_tis_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
>> +       if (size < TPM_HEADER_SIZE) {
>> +               debug("Unable to read header\n");
>> +               return size < 0 ? size : -EIO;
>> +       }
>> +
>> +       expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
>> +       if ((size_t)expected > count || (size_t)expected < TPM_HEADER_SIZE) {
>> +               debug("Error size=%x, expected=%x, count=%x\n", size, expected,
>> +                     count);
>> +               return -ENOSPC;
>> +       }
>> +
>> +       size += tpm2_tis_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
>> +                                      expected - TPM_HEADER_SIZE);
>> +       if (size < expected) {
>> +               debug("Unable to read remainder of result\n");
>> +               return -ETIMEDOUT;
>> +       }
>> +
>> +       rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c,
>> +                                       &status);
>> +       if (rc)
>> +               return rc;
>> +       if (status & TPM_STS_DATA_AVAIL) {  /* Retry? */
>> +               debug("Error left over data\n");
>> +               return -EIO;
>> +       }
>> +
>> +       return size;
>> +}
>> +
>> +static int tpm2_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +       int rc, status;
>> +       size_t burstcnt;
>> +       size_t count = 0;
>> +       int retry = 0;
>> +       u8 sts = TPM_STS_GO;
>> +
>> +       debug("%s: len=%d\n", __func__, len);
>> +       if (len > TPM_DEV_BUFSIZE)
>> +               return -E2BIG;  /* Command is too long for our tpm, sorry */
>> +
>> +       if (tpm2_tis_i2c_request_locality(dev, 0) < 0)
>> +               return -EBUSY;
>> +
>> +       status = tpm2_tis_i2c_status(dev);
>> +       if ((status & TPM_STS_COMMAND_READY) == 0) {
>> +               rc = tpm2_tis_i2c_ready(dev);
>> +               if (rc)
>> +                       return rc;
>> +               rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
>> +                                               chip->timeout_b, &status);
>> +               if (rc)
>> +                       return rc;
>> +       }
>> +
>> +       burstcnt = tpm2_tis_i2c_get_burstcount(dev);
>> +
>> +       /* burstcount < 0 -> tpm is busy */
>> +       if (burstcnt < 0)
>> +               return burstcnt;
>> +
>> +       while (count < len) {
>> +               udelay(300);
>> +               if (burstcnt > len - count)
>> +                       burstcnt = len - count;
>> +
>> +#ifdef CONFIG_TPM2_TIS_I2C_BURST_LIMITATION
>> +               if (retry && burstcnt > CONFIG_TPM2_TIS_I2C_BURST_LIMITATION_LEN)
>> +                       burstcnt = CONFIG_TPM2_TIS_I2C_BURST_LIMITATION_LEN;
>> +#endif /* CONFIG_TPM2_TIS_I2C_BURST_LIMITATION */
>> +
>> +               rc = tpm2_tis_i2c_write(dev, TPM_DATA_FIFO,
>> +                                       &(buf[count]), burstcnt);
>> +               if (rc == 0)
>> +                       count += burstcnt;
>> +               else {
>> +                       debug("%s: error\n", __func__);
>> +                       if (retry++ > 10)
>> +                               return -EIO;
>> +                       rc = tpm2_tis_i2c_wait_for_stat(dev, TPM_STS_VALID,
>> +                                                       chip->timeout_c,
>> +                                                       &status);
>> +                       if (rc)
>> +                               return rc;
>> +
>> +                       if ((status & TPM_STS_DATA_EXPECT) == 0)
>> +                               return -EIO;
>> +               }
>> +       }
>> +
>> +       /* Go and do it */
>> +       rc = tpm2_tis_i2c_write(dev, TPM_STS, &sts, 1);
>> +       if (rc < 0)
>> +               return rc;
>> +       debug("%s: done, rc=%d\n", __func__, rc);
>> +
>> +       return len;
>> +}
>> +
>> +static int tpm2_tis_i2c_cleanup(struct udevice *dev)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +
>> +       tpm2_tis_i2c_ready(dev);
>> +       /*
>> +        * The TPM needs some time to clean up here,
>> +        * so we sleep rather than keeping the bus busy
>> +        */
>> +       mdelay(2);
>> +       tpm2_tis_i2c_release_locality(dev, chip->locality, 0);
>> +
>> +       return 0;
>> +}
>> +
>> +static int tpm2_tis_i2c_init(struct udevice *dev)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +       u32 vendor;
>> +       u32 expected_did_vid;
>> +       int rc;
>> +
>> +       chip->is_open = 1;
>> +
>> +       /* Default timeouts - these could move to the device tree */
>> +       chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
>> +       chip->timeout_b = TIS_LONG_TIMEOUT_MS;
>> +       chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
>> +       chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
>> +
>> +       rc = tpm2_tis_i2c_request_locality(dev, 0);
>> +       if (rc < 0)
>> +               return rc;
>> +
>> +       /* Read four bytes from DID_VID register */
>> +       if (tpm2_tis_i2c_read(dev, TPM_DID_VID, (uchar *)&vendor, 4) < 0) {
>> +               tpm2_tis_i2c_release_locality(dev, 0, 1);
>> +               return -EIO;
>> +       }
>> +
>> +       if (chip->chip_type == NPCT75X) {
>> +               vendor = be32_to_cpu(vendor);
>> +               expected_did_vid = TPM2_TIS_I2C_DID_VID_NPCT75X;
>> +       }
>> +
>> +       if (chip->chip_type != UNKNOWN && vendor != expected_did_vid) {
>> +               pr_err("Vendor id did not match! ID was %08x\n", vendor);
>> +               return -ENODEV;
>> +       }
>> +
>> +       chip->vend_dev = vendor;
>> +       debug("2.0 TPM (chip type %s device-id 0x%X)\n",
>> +             chip_name[chip->chip_type], vendor >> 16);
>> +
>> +       /*
>> +        * A timeout query to TPM can be placed here.
>> +        * Standard timeout values are used so far
>> +        */
>> +
>> +       return 0;
>> +}
>> +
>> +static int tpm2_tis_i2c_open(struct udevice *dev)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +       int rc;
>> +
>> +       debug("%s: start\n", __func__);
>> +       if (chip->is_open)
>> +               return -EBUSY;
>> +       rc = tpm2_tis_i2c_init(dev);
>> +       if (rc < 0)
>> +               chip->is_open = 0;
>> +
>> +       return rc;
>> +}
>> +
>> +static int tpm2_tis_i2c_close(struct udevice *dev)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +
>> +       if (chip->is_open) {
>> +               tpm2_tis_i2c_release_locality(dev, chip->locality, 1);
>> +               chip->is_open = 0;
>> +               chip->vend_dev = 0;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int tpm2_tis_get_desc(struct udevice *dev, char *buf, int size)
>> +{
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +
>> +       if (size < 50)
>> +               return -ENOSPC;
>> +
>> +       return snprintf(buf, size, "2.0 TPM (%s, chip type %s device-id 0x%x)",
>> +                       chip->is_open ? "open" : "closed",
>> +                       chip_name[chip->chip_type],
>> +                       chip->vend_dev >> 16);
>> +}
>> +
>> +static int tpm2_tis_i2c_probe(struct udevice *dev)
>> +{
>> +       struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
>> +       struct tpm_chip *chip = dev_get_priv(dev);
>> +
>> +       chip->chip_type = dev_get_driver_data(dev);
>> +       chip->locality = 0;
>> +       chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
>> +       chip->timeout_b = TIS_LONG_TIMEOUT_MS;
>> +       chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
>> +       chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
>> +
>> +       /* TODO: These need to be checked and tuned */
>> +       uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
>> +       uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
>> +       uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
>> +       uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
>> +       uc_priv->pcr_count = 24;
>> +       uc_priv->pcr_select_min = 3;
>> +       uc_priv->version = TPM_V2;
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct tpm_ops tpm2_tis_i2c_ops = {
>> +       .open           = tpm2_tis_i2c_open,
>> +       .close          = tpm2_tis_i2c_close,
>> +       .get_desc       = tpm2_tis_get_desc,
>> +       .send           = tpm2_tis_i2c_send,
>> +       .recv           = tpm2_tis_i2c_recv,
>> +       .cleanup        = tpm2_tis_i2c_cleanup,
>> +};
>> +
>> +static const struct udevice_id tpm2_tis_i2c_ids[] = {
>> +       { .compatible = "nuvoton,npct75x", .data = NPCT75X },
>> +       { }
>> +};
>> +
>> +U_BOOT_DRIVER(tpm2_tis_i2c) = {
>> +       .name   = "tpm2_tis_i2c",
>> +       .id     = UCLASS_TPM,
>> +       .of_match = tpm2_tis_i2c_ids,
>> +       .ops    = &tpm2_tis_i2c_ops,
>> +       .probe  = tpm2_tis_i2c_probe,
>> +       .priv_auto_alloc_size = sizeof(struct tpm_chip),
>> +};
>> --
>> 2.27.0
>>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH u-boot v2019.04-aspeed-openbmc 5/5] aspeed: Add board_late_init to write TPM
  2022-05-10  2:44   ` Joel Stanley
@ 2022-05-11 20:13     ` Eddie James
  0 siblings, 0 replies; 12+ messages in thread
From: Eddie James @ 2022-05-11 20:13 UTC (permalink / raw)
  To: Joel Stanley; +Cc: OpenBMC Maillist


On 5/9/22 21:44, Joel Stanley wrote:
> On Thu, 5 May 2022 at 20:28, Eddie James <eajames@linux.ibm.com> wrote:
>> If there is a TPM in the devicetree, use board_late_init to
>> extend PCR0 with some invalid digest. The purpose of this is to
>> prevent later undesired usage of the TPM.
>>
>> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> I think we will need a board_late_init for our platform, as others may
> use the TPM and not want to poison it.


Yea I'll give that a go. I tried it and I had a brief fight with the 
config and build and gave up :)


>
>> ---
>>   board/aspeed/evb_ast2600/evb_ast2600.c     | 34 ++++++++++++++++++++++
>>   configs/ast2600_openbmc_spl_emmc_defconfig |  1 +
>>   2 files changed, 35 insertions(+)
>>
>> diff --git a/board/aspeed/evb_ast2600/evb_ast2600.c b/board/aspeed/evb_ast2600/evb_ast2600.c
>> index 72ecb18c15..e11fc6973d 100644
>> --- a/board/aspeed/evb_ast2600/evb_ast2600.c
>> +++ b/board/aspeed/evb_ast2600/evb_ast2600.c
>> @@ -3,6 +3,11 @@
>>    * Copyright (C) ASPEED Technology Inc.
>>    */
>>   #include <common.h>
>> +#if defined(CONFIG_TPM_V2)
>> +#include <dm/uclass.h>
>> +#include <tpm-common.h>
>> +#include <tpm-v2.h>
>> +#endif
>>   #include <asm/io.h>
>>
>>   #define SCU_BASE       0x1e6e2000
>> @@ -122,6 +127,35 @@ static void __maybe_unused espi_init(void)
>>          writel(reg, ESPI_BASE + 0x000);
>>   }
>>
>> +__weak int board_late_init(void)
>> +{
>> +#if defined(CONFIG_TPM_V2)
>> +       int rc;
>> +       struct udevice *dev;
>> +       unsigned char digest[32] = {
>> +               0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01,
>> +               0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
>> +               0xa0, 0xb1, 0xc2, 0xd3, 0xe4, 0xf5, 0x06, 0x17,
>> +               0x28, 0x39, 0x4a, 0x5b, 0x6c, 0x7d, 0x8e, 0x9f
>> +       };
> Add a comment for how this was created.
>
>> +
>> +       rc = uclass_first_device_err(UCLASS_TPM, &dev);
>> +       if (rc)
>> +               return 0;
>> +
>> +       rc = tpm_init(dev);
>> +       if (rc)
>> +               return 0;
>> +
>> +       rc = tpm2_startup(dev, TPM2_SU_CLEAR);
>> +       if (rc)
>> +               return 0;
>> +
>> +       tpm2_pcr_extend(dev, 0, digest);
>> +#endif
>> +       return 0;
>> +}
>> +
>>   int board_early_init_f(void)
>>   {
>>   #if 0
>> diff --git a/configs/ast2600_openbmc_spl_emmc_defconfig b/configs/ast2600_openbmc_spl_emmc_defconfig
>> index 3bb44280c7..b506bc5e55 100644
>> --- a/configs/ast2600_openbmc_spl_emmc_defconfig
>> +++ b/configs/ast2600_openbmc_spl_emmc_defconfig
>> @@ -39,6 +39,7 @@ CONFIG_SYS_CONSOLE_ENV_OVERWRITE=y
>>   CONFIG_DISPLAY_BOARDINFO_LATE=y
>>   CONFIG_ARCH_EARLY_INIT_R=y
>>   CONFIG_BOARD_EARLY_INIT_F=y
>> +CONFIG_BOARD_LATE_INIT=y
>>   CONFIG_SPL_BOARD_INIT=y
>>   # CONFIG_SPL_LEGACY_IMAGE_SUPPORT is not set
>>   CONFIG_SPL_SYS_MALLOC_SIMPLE=y
>> --
>> 2.27.0
>>

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2022-05-11 20:14 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-05 20:28 [PATCH u-boot v2019.04-aspeed-openbmc 0/5] ast2600: Add I2C TPMv2 driver Eddie James
2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 1/5] i2c: ast_i2c: Remove SCL direct drive mode Eddie James
2022-05-10  2:29   ` Joel Stanley
2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 2/5] tpm: Add I2C driver for TPMv2 devices Eddie James
2022-05-10  2:42   ` Joel Stanley
2022-05-11 20:10     ` Eddie James
2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 3/5] arm: dts: ast2600-rainier: Add NPCT75X TPM Eddie James
2022-05-10  2:44   ` Joel Stanley
2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 4/5] configs: ast2600_openbmc_spl_emmc: Enable TPMv2 over I2C Eddie James
2022-05-05 20:28 ` [PATCH u-boot v2019.04-aspeed-openbmc 5/5] aspeed: Add board_late_init to write TPM Eddie James
2022-05-10  2:44   ` Joel Stanley
2022-05-11 20:13     ` Eddie James

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.