aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/i2c/tpm/tis_atmel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/i2c/tpm/tis_atmel.c')
-rw-r--r--src/drivers/i2c/tpm/tis_atmel.c132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/drivers/i2c/tpm/tis_atmel.c b/src/drivers/i2c/tpm/tis_atmel.c
new file mode 100644
index 0000000000..7d8b7a17b8
--- /dev/null
+++ b/src/drivers/i2c/tpm/tis_atmel.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/early_variables.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <commonlib/endian.h>
+#include <console/console.h>
+#include <delay.h>
+#include <device/i2c.h>
+#include <endian.h>
+#include <lib.h>
+#include <tpm.h>
+#include <timer.h>
+
+#define RECV_TIMEOUT (1 * 1000) /* 1 second */
+#define XMIT_TIMEOUT (1 * 1000) /* 1 second */
+#define SLEEP_DURATION 1000 /* microseconds */
+
+struct tpm_output_header {
+ uint16_t tag;
+ uint32_t length;
+ uint32_t return_code;
+} __attribute__ ((packed));
+
+int tis_open(void)
+{
+ return 0;
+}
+
+int tis_close(void)
+{
+ return 0;
+}
+
+int tis_init(void)
+{
+ return 0;
+}
+
+int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
+ uint8_t *recvbuf, size_t *rbuf_len)
+{
+ size_t hdr_bytes;
+ struct tpm_output_header *header;
+ size_t max_recv_bytes;
+ size_t recv_bytes;
+ int status;
+ struct stopwatch sw;
+
+ ASSERT(sbuf_size >= 10);
+ if (IS_ENABLED(CONFIG_DRIVER_TPM_DISPLAY_TIS_BYTES)) {
+ /* Display the TPM command */
+ if (sbuf_size >= 10)
+ printk(BIOS_DEBUG, "TPM Command: 0x%08x\n",
+ read_at_be32(sendbuf, sizeof(uint16_t)
+ + sizeof(uint32_t)));
+ hexdump(sendbuf, sbuf_size);
+ }
+
+ /* Send the command to the TPM */
+ stopwatch_init_msecs_expire(&sw, XMIT_TIMEOUT);
+ while (1) {
+ status = i2c_write_raw(CONFIG_DRIVER_TPM_I2C_BUS,
+ CONFIG_DRIVER_TPM_I2C_ADDR, (uint8_t *)sendbuf,
+ sbuf_size);
+ if ((status < 0) && (!stopwatch_expired(&sw)))
+ continue;
+ if (status < 0)
+ return status;
+ break;
+ }
+
+ /* Read the TPM response header */
+ max_recv_bytes = *rbuf_len;
+ ASSERT(max_recv_bytes >= sizeof(*header));
+ hdr_bytes = sizeof(*header);
+ header = (struct tpm_output_header *)recvbuf;
+ stopwatch_init_msecs_expire(&sw, RECV_TIMEOUT);
+ do {
+ status = i2c_read_raw(CONFIG_DRIVER_TPM_I2C_BUS,
+ CONFIG_DRIVER_TPM_I2C_ADDR, recvbuf, hdr_bytes);
+ if (status > 0)
+ break;
+ udelay(SLEEP_DURATION);
+ } while (!stopwatch_expired(&sw));
+ if (status != sizeof(*header))
+ return -1;
+
+ /* Determine the number of bytes remaining */
+ recv_bytes = min(be32_to_cpu(*(uint32_t *)&header->length),
+ max_recv_bytes);
+
+ /* Determine if there is additional response data */
+ if (recv_bytes > hdr_bytes) {
+ /* Display the TPM response */
+ if (IS_ENABLED(CONFIG_DRIVER_TPM_DISPLAY_TIS_BYTES))
+ hexdump(recvbuf, hdr_bytes);
+
+ /* Read the full TPM response */
+ status = i2c_read_raw(CONFIG_DRIVER_TPM_I2C_BUS,
+ CONFIG_DRIVER_TPM_I2C_ADDR, recvbuf, recv_bytes);
+ if (status < 0)
+ return status;
+ }
+
+ /* Return the number of bytes received */
+ *rbuf_len = status;
+
+ /* Display the TPM response */
+ if (IS_ENABLED(CONFIG_DRIVER_TPM_DISPLAY_TIS_BYTES)) {
+ printk(BIOS_DEBUG, "TPM Response: 0x%08x\n",
+ read_at_be32(recvbuf, sizeof(uint16_t)
+ + sizeof(uint32_t)));
+ hexdump(recvbuf, *rbuf_len);
+ }
+
+ /* Successful transfer */
+ return 0;
+}