feat(tpm): add tpm drivers and framework
Add tpm2 drivers to tf-a with adequate framework
-implement a fifo spi interface that works
with discrete tpm chip.
-implement tpm command layer interfaces that are used
to initialize, start and make measurements and
close the interface.
-tpm drivers are built using their own make file
to allow for ease in porting across platforms,
and across different interfaces.
Signed-off-by: Tushar Khandelwal <tushar.khandelwal@arm.com>
Signed-off-by: Abhi Singh <abhi.singh@arm.com>
Change-Id: Ie1a189f45c80f26f4dea16c3bd71b1503709e0ea
diff --git a/drivers/tpm/tpm2.mk b/drivers/tpm/tpm2.mk
new file mode 100644
index 0000000..4418b97
--- /dev/null
+++ b/drivers/tpm/tpm2.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (c) 2025, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+TPM2_SRC_DIR := drivers/tpm/
+
+TPM2_SOURCES := ${TPM2_SRC_DIR}tpm2_cmds.c \
+ ${TPM2_SRC_DIR}tpm2_chip.c
+
+# TPM Hash algorithm, used during Measured Boot
+# currently only accepts SHA-256
+ifeq (${MBOOT_TPM_HASH_ALG}, sha256)
+ TPM_ALG_ID := TPM_ALG_SHA256
+ TCG_DIGEST_SIZE := 32U
+else
+ $(error "The selected MBOOT_TPM_HASH_ALG is invalid.")
+endif #MBOOT_TPM_HASH_ALG
+
+ifeq (${TPM_INTERFACE}, FIFO_SPI)
+ TPM2_SOURCES += ${TPM2_SRC_DIR}tpm2_fifo.c \
+ ${TPM2_SRC_DIR}tpm2_fifo_spi.c
+else
+ $(error "The selected TPM_INTERFACE is invalid.")
+endif #TPM_INTERFACE
diff --git a/drivers/tpm/tpm2_chip.c b/drivers/tpm/tpm2_chip.c
new file mode 100644
index 0000000..537ce92
--- /dev/null
+++ b/drivers/tpm/tpm2_chip.c
@@ -0,0 +1,21 @@
+
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <drivers/tpm/tpm2_chip.h>
+
+/*
+ * TPM timeout values
+ * Reference: TCG PC Client Platform TPM Profile (PTP) Specification v1.05
+ */
+struct tpm_chip_data tpm_chip_data = {
+ .locality = -1,
+ .timeout_msec_a = 750,
+ .timeout_msec_b = 2000,
+ .timeout_msec_c = 200,
+ .timeout_msec_d = 30,
+ .address = 0,
+};
diff --git a/drivers/tpm/tpm2_cmds.c b/drivers/tpm/tpm2_cmds.c
new file mode 100644
index 0000000..b6422a8
--- /dev/null
+++ b/drivers/tpm/tpm2_cmds.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <lib/libc/endian.h>
+
+#include <drivers/delay_timer.h>
+#include <drivers/tpm/tpm2.h>
+#include <drivers/tpm/tpm2_chip.h>
+#include <drivers/tpm/tpm2_interface.h>
+
+#define CMD_SIZE_OFFSET 6
+
+#define SINGLE_BYTE 1
+#define TWO_BYTES 2
+#define FOUR_BYTES 4
+
+static struct interface_ops *interface;
+
+static int tpm_xfer(struct tpm_chip_data *chip_data, const tpm_cmd *send, tpm_cmd *receive)
+{
+ int ret;
+
+ ret = interface->send(chip_data, send);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = interface->receive(chip_data, receive);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return TPM_SUCCESS;
+}
+
+int tpm_interface_init(struct tpm_chip_data *chip_data, uint8_t locality)
+{
+ int err;
+
+ interface = tpm_interface_getops(chip_data, locality);
+
+ err = interface->request_access(chip_data, locality);
+ if (err != 0) {
+ return err;
+ }
+
+ return interface->get_info(chip_data, locality);
+}
+
+int tpm_interface_close(struct tpm_chip_data *chip_data, uint8_t locality)
+{
+ return interface->release_locality(chip_data, locality);
+}
+
+static int tpm_update_buffer(tpm_cmd *buf, uint32_t new_data, size_t new_len)
+{
+ int i, j, start;
+ uint32_t command_size;
+
+ union {
+ uint8_t var8;
+ uint16_t var16;
+ uint32_t var32;
+ uint8_t array[4];
+ } tpm_new_data;
+
+ command_size = be32toh(buf->header.cmd_size);
+
+ if (command_size + new_len > MAX_SIZE_CMDBUF) {
+ ERROR("%s: buf size exceeded, increase MAX_SIZE_CMDBUF\n",
+ __func__);
+ return TPM_INVALID_PARAM;
+ }
+ /*
+ * Subtract the cmd header size from the current command size
+ * so the data buffer is written to starting at index 0.
+ */
+ start = command_size - TPM_HEADER_SIZE;
+
+ /*
+ * The TPM, according to the TCG spec, processes data in BE byte order,
+ * in the case where the Host is LE, htobe correctly handles the byte order.
+ * When updating the buffer, keep in mind to only pass sizeof(new_data) or
+ * the variable type size for the new_len function parameter. This ensures
+ * there is only the possiblility of writing 1, 2, or 4 bytes to the buffer,
+ * and that the correct number of bytes are written to data[i].
+ */
+ if (new_len == SINGLE_BYTE) {
+ tpm_new_data.var8 = new_data & 0xFF;
+ } else if (new_len == TWO_BYTES) {
+ tpm_new_data.var16 = htobe16(new_data & 0xFFFF);
+ } else if (new_len == FOUR_BYTES) {
+ tpm_new_data.var32 = htobe32(new_data);
+ } else {
+ ERROR("%s: Invalid data length\n", __func__);
+ return TPM_INVALID_PARAM;
+ }
+
+ for (i = start, j = 0; i < start + new_len; i++, j++) {
+ buf->data[i] = tpm_new_data.array[j];
+ }
+ buf->header.cmd_size = htobe32(command_size + new_len);
+
+ return TPM_SUCCESS;
+}
+
+
+int tpm_startup(struct tpm_chip_data *chip_data, uint16_t mode)
+{
+ tpm_cmd startup_cmd, startup_response;
+ uint32_t tpm_rc;
+ int ret;
+
+ memset(&startup_cmd, 0, sizeof(startup_cmd));
+ memset(&startup_response, 0, sizeof(startup_response));
+
+ startup_cmd.header.tag = htobe16(TPM_ST_NO_SESSIONS);
+ startup_cmd.header.cmd_size = htobe32(sizeof(tpm_cmd_hdr));
+ startup_cmd.header.cmd_code = htobe32(TPM_CMD_STARTUP);
+
+ ret = tpm_update_buffer(&startup_cmd, mode, sizeof(mode));
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = tpm_xfer(chip_data, &startup_cmd, &startup_response);
+ if (ret < 0) {
+ return ret;
+ }
+
+ tpm_rc = be32toh(startup_response.header.cmd_code);
+ if (tpm_rc != TPM_RESPONSE_SUCCESS) {
+ ERROR("%s: response code contains error = %X\n", __func__, tpm_rc);
+ return TPM_ERR_RESPONSE;
+ }
+
+ return TPM_SUCCESS;
+}
+
+int tpm_pcr_extend(struct tpm_chip_data *chip_data, uint32_t index,
+ uint16_t algorithm, const uint8_t *digest,
+ uint32_t digest_len)
+{
+ tpm_cmd pcr_extend_cmd, pcr_extend_response;
+ uint32_t tpm_rc;
+ int ret;
+
+ memset(&pcr_extend_cmd, 0, sizeof(pcr_extend_cmd));
+ memset(&pcr_extend_response, 0, sizeof(pcr_extend_response));
+
+ if (digest == NULL) {
+ return TPM_INVALID_PARAM;
+ }
+ pcr_extend_cmd.header.tag = htobe16(TPM_ST_SESSIONS);
+ pcr_extend_cmd.header.cmd_size = htobe32(sizeof(tpm_cmd_hdr));
+ pcr_extend_cmd.header.cmd_code = htobe32(TPM_CMD_PCR_EXTEND);
+
+ /* handle (PCR Index)*/
+ ret = tpm_update_buffer(&pcr_extend_cmd, index, sizeof(index));
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* authorization size , session handle, nonce size, attributes*/
+ ret = tpm_update_buffer(&pcr_extend_cmd, TPM_MIN_AUTH_SIZE, sizeof(uint32_t));
+ if (ret < 0) {
+ return ret;
+ }
+ ret = tpm_update_buffer(&pcr_extend_cmd, TPM_RS_PW, sizeof(uint32_t));
+ if (ret < 0) {
+ return ret;
+ }
+ ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ZERO_NONCE_SIZE, sizeof(uint16_t));
+ if (ret < 0) {
+ return ret;
+ }
+ ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ATTRIBUTES_DISABLE, sizeof(uint8_t));
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* hmac/password size */
+ ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ZERO_HMAC_SIZE, sizeof(uint16_t));
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* hashes count */
+ ret = tpm_update_buffer(&pcr_extend_cmd, TPM_SINGLE_HASH_COUNT, sizeof(uint32_t));
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* hash algorithm */
+ ret = tpm_update_buffer(&pcr_extend_cmd, algorithm, sizeof(algorithm));
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* digest */
+ for (int i = 0; i < digest_len; i++) {
+ ret = tpm_update_buffer(&pcr_extend_cmd, digest[i], sizeof(uint8_t));
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ ret = tpm_xfer(chip_data, &pcr_extend_cmd, &pcr_extend_response);
+ if (ret < 0) {
+ return ret;
+ }
+
+ tpm_rc = be32toh(pcr_extend_response.header.cmd_code);
+ if (tpm_rc != TPM_RESPONSE_SUCCESS) {
+ ERROR("%s: response code contains error = %X\n", __func__, tpm_rc);
+ return TPM_ERR_RESPONSE;
+ }
+
+ return TPM_SUCCESS;
+}
diff --git a/drivers/tpm/tpm2_fifo.c b/drivers/tpm/tpm2_fifo.c
new file mode 100644
index 0000000..7c4b9d8
--- /dev/null
+++ b/drivers/tpm/tpm2_fifo.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <lib/libc/endian.h>
+
+#include <drivers/delay_timer.h>
+#include <drivers/tpm/tpm2.h>
+#include <drivers/tpm/tpm2_chip.h>
+#include <drivers/tpm/tpm2_interface.h>
+
+#define LOCALITY_START_ADDRESS(x, y) \
+ ((uint16_t)(x->address + (0x1000 * y)))
+
+static int tpm2_get_info(struct tpm_chip_data *chip_data, uint8_t locality)
+{
+ uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality);
+ uint32_t vid_did;
+ uint8_t revision;
+ int err;
+
+ err = tpm2_fifo_read_chunk(tpm_base_addr + TPM_FIFO_REG_VENDID, DWORD, &vid_did);
+ if (err < 0) {
+ return err;
+ }
+
+ err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_REVID, &revision);
+ if (err < 0) {
+ return err;
+ }
+
+ INFO("TPM Chip: vendor-id 0x%x, device-id 0x%x, revision-id: 0x%x\n",
+ 0xFFFF & vid_did, vid_did >> 16, revision);
+
+ return TPM_SUCCESS;
+}
+
+static int tpm2_wait_reg_bits(uint16_t reg, uint8_t set, unsigned long timeout, uint8_t *status)
+{
+ int err;
+ uint64_t timeout_delay = timeout_init_us(timeout * 1000);
+
+ do {
+ err = tpm2_fifo_read_byte(reg, status);
+ if (err < 0) {
+ return err;
+ }
+ if ((*status & set) == set) {
+ return TPM_SUCCESS;
+ }
+ } while (!timeout_elapsed(timeout_delay));
+
+ return TPM_ERR_TIMEOUT;
+}
+
+static int tpm2_fifo_request_access(struct tpm_chip_data *chip_data, uint8_t locality)
+{
+ uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality);
+ uint8_t status;
+ int err;
+
+ err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, TPM_ACCESS_REQUEST_USE);
+ if (err < 0) {
+ return err;
+ }
+
+ err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_ACCESS,
+ TPM_ACCESS_ACTIVE_LOCALITY,
+ chip_data->timeout_msec_a, &status);
+ if (err == 0) {
+ chip_data->locality = locality;
+ return TPM_SUCCESS;
+ }
+
+ return err;
+}
+
+static int tpm2_fifo_release_locality(struct tpm_chip_data *chip_data, uint8_t locality)
+{
+ uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality);
+ uint8_t buf;
+ int err;
+
+ err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, &buf);
+ if (err < 0) {
+ return err;
+ }
+
+ if (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
+ return tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS,
+ TPM_ACCESS_RELINQUISH_LOCALITY);
+ }
+
+ ERROR("%s: Unable to release locality\n", __func__);
+ return TPM_ERR_RESPONSE;
+}
+
+static int tpm2_fifo_prepare(struct tpm_chip_data *chip_data)
+{
+ uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality);
+ uint8_t status;
+ int err;
+
+ err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_COMMAND_READY);
+ if (err < 0) {
+ return err;
+ }
+
+ err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS,
+ TPM_STAT_COMMAND_READY,
+ chip_data->timeout_msec_b, &status);
+ if (err < 0) {
+ ERROR("%s: TPM Status Busy\n", __func__);
+ return err;
+ }
+
+ return TPM_SUCCESS;
+}
+
+static int tpm2_fifo_get_burstcount(struct tpm_chip_data *chip_data, uint16_t *burstcount)
+{
+ uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality);
+ uint64_t timeout_delay = timeout_init_us(chip_data->timeout_msec_a * 1000);
+ int err;
+
+ if (burstcount == NULL) {
+ return TPM_INVALID_PARAM;
+ }
+
+ do {
+ uint8_t byte0, byte1;
+
+ err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_LO, &byte0);
+ if (err < 0) {
+ return err;
+ }
+
+ err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_HI, &byte1);
+ if (err < 0) {
+ return err;
+ }
+
+ *burstcount = (uint16_t)((byte1 << 8) + byte0);
+ if (*burstcount != 0U) {
+ return TPM_SUCCESS;
+ }
+ } while (!timeout_elapsed(timeout_delay));
+
+ return TPM_ERR_TIMEOUT;
+}
+
+static int tpm2_fifo_send(struct tpm_chip_data *chip_data, const tpm_cmd *buf)
+{
+ uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality);
+ uint8_t status;
+ uint16_t burstcnt;
+ int err;
+ uint32_t len, index;
+
+ if (sizeof(buf->header) != TPM_HEADER_SIZE) {
+ ERROR("%s: invalid command header size.\n", __func__);
+ return TPM_INVALID_PARAM;
+ }
+
+ err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, &status);
+ if (err < 0) {
+ return err;
+ }
+
+ if (!(status & TPM_STAT_COMMAND_READY)) {
+ err = tpm2_fifo_prepare(chip_data);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ /* write the command header to the TPM first */
+ const uint8_t *header_data = (const uint8_t *)&buf->header;
+
+ for (index = 0; index < TPM_HEADER_SIZE; index++) {
+ err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO,
+ header_data[index]);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ len = be32toh(buf->header.cmd_size);
+
+ while (index < len) {
+ err = tpm2_fifo_get_burstcount(chip_data, &burstcnt);
+ if (err < 0) {
+ return err;
+ }
+
+ for (; burstcnt > 0U && index < len; burstcnt--) {
+ err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO,
+ buf->data[index - TPM_HEADER_SIZE]);
+ if (err < 0) {
+ return err;
+ }
+ index++;
+ }
+ }
+
+ err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS,
+ TPM_STAT_VALID,
+ chip_data->timeout_msec_c,
+ &status);
+ if (err < 0) {
+ return err;
+ }
+
+ if (status & TPM_STAT_EXPECT) {
+ ERROR("%s: TPM is still expecting data after command buffer is sent\n", __func__);
+ return TPM_ERR_TRANSFER;
+ }
+
+ err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_GO);
+ if (err < 0) {
+ return err;
+ }
+
+ return TPM_SUCCESS;
+}
+
+static int tpm2_fifo_read_data(struct tpm_chip_data *chip_data, tpm_cmd *buf,
+ uint16_t tpm_base_addr, uint8_t *status, int *size, int bytes_expected)
+{
+ int err, read_size, loop_index;
+ uint16_t burstcnt;
+ uint8_t *read_data;
+
+ if (bytes_expected == TPM_READ_HEADER) {
+ /* read the response header from the TPM first */
+ read_data = (uint8_t *)&buf->header;
+ read_size = TPM_HEADER_SIZE;
+ loop_index = *size;
+ } else {
+ /* process the rest of the mssg with bytes_expected */
+ read_data = buf->data;
+ read_size = bytes_expected;
+ loop_index = *size - TPM_HEADER_SIZE;
+ }
+
+ err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS,
+ TPM_STAT_AVAIL,
+ chip_data->timeout_msec_c,
+ status);
+ if (err < 0) {
+ return err;
+ }
+
+ while (*size < read_size) {
+ err = tpm2_fifo_get_burstcount(chip_data, &burstcnt);
+ if (err < 0) {
+ ERROR("%s: TPM burst count error\n", __func__);
+ return err;
+ }
+
+ for (; burstcnt > 0U && loop_index < read_size;
+ burstcnt--, loop_index++, (*size)++) {
+ err = tpm2_fifo_read_byte(
+ tpm_base_addr + TPM_FIFO_REG_DATA_FIFO,
+ (void *)&read_data[loop_index]);
+ if (err < 0) {
+ return err;
+ }
+ }
+ }
+
+ return TPM_SUCCESS;
+}
+
+static int tpm2_fifo_receive(struct tpm_chip_data *chip_data, tpm_cmd *buf)
+{
+ uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality);
+ int size = 0, bytes_expected, err;
+ uint8_t status;
+
+ err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, TPM_READ_HEADER);
+ if (err < 0) {
+ return err;
+ }
+
+ bytes_expected = be32toh(buf->header.cmd_size);
+ if (bytes_expected > sizeof(*buf)) {
+ ERROR("%s: tpm response buffer cannot store expected response\n", __func__);
+ return TPM_INVALID_PARAM;
+ }
+
+ if (size == bytes_expected) {
+ return size;
+ }
+
+ err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, bytes_expected);
+ if (err < 0) {
+ return err;
+ }
+
+ if (size < bytes_expected) {
+ ERROR("%s: response buffer size is less than expected\n", __func__);
+ return TPM_ERR_RESPONSE;
+ }
+
+ return TPM_SUCCESS;
+}
+
+static interface_ops_t fifo_ops = {
+ .get_info = tpm2_get_info,
+ .send = tpm2_fifo_send,
+ .receive = tpm2_fifo_receive,
+ .request_access = tpm2_fifo_request_access,
+ .release_locality = tpm2_fifo_release_locality,
+};
+
+struct interface_ops *
+tpm_interface_getops(struct tpm_chip_data *chip_data, uint8_t locality)
+{
+ return &fifo_ops;
+}
diff --git a/drivers/tpm/tpm2_fifo_spi.c b/drivers/tpm/tpm2_fifo_spi.c
new file mode 100644
index 0000000..16d87d9
--- /dev/null
+++ b/drivers/tpm/tpm2_fifo_spi.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <drivers/gpio_spi.h>
+#include <drivers/tpm/tpm2.h>
+#include <drivers/tpm/tpm2_chip.h>
+#include <drivers/tpm/tpm2_interface.h>
+
+#define ENCODE_LIMIT 128
+#define CS_ASSERT_OFFSET 0xD4
+#define RETRY_COUNT 50
+
+#define TPM_READ false
+#define TPM_WRITE true
+
+extern struct spi_plat *spidev;
+
+static int tpm2_spi_transfer(const void *data_out, void *data_in, uint8_t len)
+{
+ return spidev->ops->xfer(len, data_out, data_in);
+}
+
+/*
+ * Reference: TCG PC Client Platform TPM Profile (PTP) Specification v1.05
+ */
+static int tpm2_spi_start_transaction(uint16_t tpm_reg, bool write, uint8_t len)
+{
+ int rc;
+ uint8_t header[4];
+ uint8_t header_response[4];
+ uint8_t zero = 0, byte;
+ int retries;
+
+ /* check to make sure len does not exceed the encoding limit */
+ if (len > ENCODE_LIMIT) {
+ return TPM_INVALID_PARAM;
+ }
+
+ /*
+ * 7.4.6 TPM SPI Bit protocol calls for the following header
+ * to be sent to the TPM at the start of every attempted read/write.
+ */
+
+ /* header[0] contains the r/w and the xfer size, if the msb is not
+ * set, the operation is write, if it is set then it is read.
+ * The size of the transfer is encoded, and must not overwrite
+ * the msb, therefore an ENCODE LIMIT of 128 is present.
+ */
+ header[0] = ((write) ? 0x00 : 0x80) | (len - 1);
+
+ /*
+ * header[1] contains the address offset 0xD4_xxxx as defined
+ * in the TPM spec, since the CS# is asserted.
+ */
+ header[1] = CS_ASSERT_OFFSET;
+
+ /*
+ * header[2] and header[3] contain the address of the register
+ * to be read/written.
+ */
+ header[2] = tpm_reg >> 8;
+ header[3] = tpm_reg;
+
+ rc = tpm2_spi_transfer(header, header_response, 4);
+ if (rc != 0) {
+ return TPM_ERR_TRANSFER;
+ }
+
+ /*
+ * 7.4.5 Flow Control defines a wait state in order to accommodate
+ * the TPM in case it needs to free its buffer.
+ */
+ if ((header_response[3] & 0x01) != 0U) {
+ return TPM_SUCCESS;
+ }
+
+ /*
+ * if the wait state over bit is not set in the initial header_response,
+ * poll for the wait state over by sending a zeroed byte, if the
+ * RETRY_COUNT is exceeded the transfer fails.
+ */
+ for (retries = RETRY_COUNT; retries > 0; retries--) {
+ rc = tpm2_spi_transfer(&zero, &byte, 1);
+ if (rc != 0) {
+ return TPM_ERR_TRANSFER;
+ }
+ if ((byte & 0x01) != 0U) {
+ return TPM_SUCCESS;
+ }
+ }
+
+ if (retries == 0) {
+ ERROR("%s: TPM Timeout\n", __func__);
+ return TPM_ERR_TIMEOUT;
+ }
+
+ return TPM_SUCCESS;
+}
+
+static void tpm2_spi_end_transaction(void)
+{
+ spidev->ops->stop();
+}
+
+static void tpm2_spi_init(void)
+{
+ spidev->ops->get_access();
+ spidev->ops->start();
+}
+
+static int tpm2_fifo_io(uint16_t tpm_reg, bool is_write, uint8_t len, void *val)
+{
+ int rc;
+
+ tpm2_spi_init();
+ rc = tpm2_spi_start_transaction(tpm_reg, is_write, len);
+ if (rc != 0) {
+ tpm2_spi_end_transaction();
+ return rc;
+ }
+
+ rc = tpm2_spi_transfer(
+ is_write ? val : NULL,
+ is_write ? NULL : val,
+ len);
+ if (rc != 0) {
+ tpm2_spi_end_transaction();
+ return rc;
+ }
+
+ tpm2_spi_end_transaction();
+
+ return TPM_SUCCESS;
+}
+
+int tpm2_fifo_write_byte(uint16_t tpm_reg, uint8_t val)
+{
+ return tpm2_fifo_io(tpm_reg, TPM_WRITE, BYTE, &val);
+}
+
+int tpm2_fifo_read_byte(uint16_t tpm_reg, uint8_t *val)
+{
+ return tpm2_fifo_io(tpm_reg, TPM_READ, BYTE, val);
+}
+
+int tpm2_fifo_read_chunk(uint16_t tpm_reg, uint8_t len, void *val)
+{
+ if ((len != BYTE) && (len != WORD) && (len != DWORD)) {
+ return TPM_INVALID_PARAM;
+ }
+
+ return tpm2_fifo_io(tpm_reg, TPM_READ, len, val);
+}