Add TPM framework support

Ported from Coreboot:
https://chromium.googlesource.com/chromiumos/third_party/coreboot/+/chromeos-2013.04/src/drivers/i2c/tpm/
d5ab28b5d73c03adcdc0fd4e530b39a7a8989dae

Change-Id: I42501e659007332e3b1d36828d47759bacc2c0f7
diff --git a/drivers/Kconfig b/drivers/Kconfig
index db351f7..eb7d815 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -14,5 +14,6 @@
 source "drivers/mfd/Kconfig"
 source "drivers/led/Kconfig"
 source "drivers/otp/Kconfig"
+source "drivers/tpm/Kconfig"
 
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 7ee324e..a7cce32 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -13,4 +13,5 @@
 obj-$(CONFIG_LED) += led/
 obj-y += otp/
 obj-$(CONFIG_COMCERTO_SATA)	+= sata/
+obj-y += tpm/
 
diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
new file mode 100644
index 0000000..ab98f2a
--- /dev/null
+++ b/drivers/tpm/Kconfig
@@ -0,0 +1,7 @@
+menu "TPM driver                "
+
+config TPM
+	bool "Enable TPM driver support"
+	default n
+
+endmenu
diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
new file mode 100644
index 0000000..443ce91
--- /dev/null
+++ b/drivers/tpm/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_TPM) += tis.o
diff --git a/drivers/tpm/tis.c b/drivers/tpm/tis.c
new file mode 100644
index 0000000..7c9a89b
--- /dev/null
+++ b/drivers/tpm/tis.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ * Copyright 2013 Google Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <asm/byteorder.h>
+#include <common.h>
+#include <stdint.h>
+#include <string.h>
+#include <tpm.h>
+#include "tpm.h"
+
+#ifdef DEBUG
+#include <assert.h>
+#define ASSERT(x) assert(x)
+#else
+#define ASSERT(x) ((void)0)
+#endif
+
+#define BIOS_DEBUG KERN_DEBUG
+
+/* global structure for tpm chip data */
+struct tpm_chip g_chip;
+
+#define TPM_CMD_COUNT_BYTE 2
+#define TPM_CMD_ORDINAL_BYTE 6
+#define TPM_VALID_STATUS (1 << 7)
+
+int tis_open(void)
+{
+	if (!g_chip.is_open) {
+		printk(BIOS_DEBUG "TPM is not available.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int tis_close(void)
+{
+	return 0;
+}
+
+int tis_init(void)
+{
+	if (!g_chip.is_open) {
+		printk(BIOS_DEBUG "TPM is not available.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static ssize_t tpm_transmit(const uint8_t *buf, size_t bufsiz)
+{
+	int rc;
+	uint32_t count, ordinal;
+
+	struct tpm_chip *chip = &g_chip;
+
+	memcpy(&count, buf + TPM_CMD_COUNT_BYTE, sizeof(count));
+	count = be32_to_cpu(count);
+	memcpy(&ordinal, buf + TPM_CMD_ORDINAL_BYTE, sizeof(ordinal));
+	ordinal = be32_to_cpu(ordinal);
+
+	if (count == 0) {
+		printk(BIOS_DEBUG "tpm_transmit: no data\n");
+		return -1;
+	}
+	if (count > bufsiz) {
+		printk(BIOS_DEBUG "tpm_transmit: invalid count value %x %zx\n",
+			count, bufsiz);
+		return -1;
+	}
+
+	ASSERT(chip->vendor.send);
+	rc = chip->vendor.send(chip, (uint8_t *) buf, count);
+	if (rc < 0) {
+		printk(BIOS_DEBUG "tpm_transmit: tpm_send error\n");
+		goto out;
+	}
+
+	if (chip->vendor.irq)
+		goto out_recv;
+
+	int timeout = 2 * 60 * 1000; /* two minutes timeout */
+	while (timeout) {
+		ASSERT(chip->vendor.status);
+		uint8_t status = chip->vendor.status(chip);
+		if ((status & chip->vendor.req_complete_mask) ==
+		    chip->vendor.req_complete_val) {
+			goto out_recv;
+		}
+
+		if ((status == chip->vendor.req_canceled)) {
+			printk(BIOS_DEBUG "tpm_transmit: Operation Canceled\n");
+			rc = -1;
+			goto out;
+		}
+		mdelay(TPM_TIMEOUT);
+		timeout--;
+	}
+
+	ASSERT(chip->vendor.cancel);
+	chip->vendor.cancel(chip);
+	printk(BIOS_DEBUG "tpm_transmit: Operation Timed out\n");
+	rc = -1; //ETIME;
+	goto out;
+
+out_recv:
+
+	rc = chip->vendor.recv(chip, (uint8_t *) buf, TPM_BUFSIZE);
+	if (rc < 0)
+		printk(BIOS_DEBUG "tpm_transmit: tpm_recv: error %d\n", rc);
+out:
+	return rc;
+}
+
+int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
+		uint8_t *recvbuf, size_t *rbuf_len)
+{
+	uint8_t buf[TPM_BUFSIZE];
+
+	if (sizeof(buf) < sbuf_size)
+		return -1;
+
+	memcpy(buf, sendbuf, sbuf_size);
+
+	int len = tpm_transmit(buf, sbuf_size);
+
+	if (len < 10) {
+		*rbuf_len = 0;
+		return -1;
+	}
+
+	if (len > *rbuf_len) {
+		*rbuf_len = len;
+		return -1;
+	}
+
+	memcpy(recvbuf, buf, len);
+	*rbuf_len = len;
+
+	return 0;
+}
diff --git a/drivers/tpm/tpm.h b/drivers/tpm/tpm.h
new file mode 100644
index 0000000..a34a0d5
--- /dev/null
+++ b/drivers/tpm/tpm.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external@infineon.com>
+ *
+ * Version: 2.1.1
+ *
+ * Description:
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * It is based on the Linux kernel driver tpm.c from Leendert van
+ * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
+ *
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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, version 2 of the
+ * License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DRIVERS_TPM_TPM_H__
+#define __DRIVERS_TPM_TPM_H__
+
+#include <stdint.h>
+
+enum tpm_timeout {
+	TPM_TIMEOUT = 1,	/* msecs */
+};
+
+/* Size of external transmit buffer (used for stack buffer in tpm_sendrecv) */
+#define TPM_BUFSIZE 1260
+
+/* Index of fields in TPM command buffer */
+#define TPM_CMD_SIZE_BYTE 2
+#define TPM_CMD_ORDINAL_BYTE 6
+
+/* Index of Count field in TPM response buffer */
+#define TPM_RSP_SIZE_BYTE 2
+#define TPM_RSP_RC_BYTE 6
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+	const uint8_t req_complete_mask;
+	const uint8_t req_complete_val;
+	const uint8_t req_canceled;
+	int irq;
+	int (*recv)(struct tpm_chip *, uint8_t *, size_t);
+	int (*send)(struct tpm_chip *, uint8_t *, size_t);
+	void (*cancel)(struct tpm_chip *);
+	uint8_t(*status)(struct tpm_chip *);
+	int locality;
+};
+
+struct tpm_chip {
+	int is_open;
+	struct tpm_vendor_specific vendor;
+};
+
+struct tpm_input_header {
+	uint16_t tag;
+	uint32_t length;
+	uint32_t ordinal;
+} __attribute__ ((packed));
+
+struct tpm_output_header {
+	uint16_t tag;
+	uint32_t length;
+	uint32_t return_code;
+} __attribute__ ((packed));
+
+struct timeout_t {
+	uint32_t a;
+	uint32_t b;
+	uint32_t c;
+	uint32_t d;
+} __attribute__ ((packed));
+
+struct duration_t {
+	uint32_t tpm_short;
+	uint32_t tpm_medium;
+	uint32_t tpm_long;
+} __attribute__ ((packed));
+
+typedef union {
+	struct timeout_t timeout;
+	struct duration_t duration;
+} cap_t;
+
+struct tpm_getcap_params_in {
+	uint32_t cap;
+	uint32_t subcap_size;
+	uint32_t subcap;
+} __attribute__ ((packed));
+
+struct tpm_getcap_params_out {
+	uint32_t cap_size;
+	cap_t cap;
+} __attribute__ ((packed));
+
+typedef union {
+	struct tpm_input_header in;
+	struct tpm_output_header out;
+} tpm_cmd_header;
+
+typedef union {
+	struct tpm_getcap_params_out getcap_out;
+	struct tpm_getcap_params_in getcap_in;
+} tpm_cmd_params;
+
+struct tpm_cmd_t {
+	tpm_cmd_header header;
+	tpm_cmd_params params;
+} __attribute__ ((packed));
+
+#endif /* __DRIVERS_TPM_H__ */
diff --git a/include/tpm.h b/include/tpm.h
new file mode 100644
index 0000000..e25ad6a
--- /dev/null
+++ b/include/tpm.h
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2011 Google Inc.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef TPM_H_
+#define TPM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * tis_init()
+ *
+ * Initialize the TPM device. Returns 0 on success or -1 on
+ * failure (in case device probing did not succeed).
+ */
+int tis_init(void);
+
+/*
+ * tis_open()
+ *
+ * Requests access to locality 0 for the caller. After all commands have been
+ * completed the caller is supposed to call tis_close().
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int tis_open(void);
+
+/*
+ * tis_close()
+ *
+ * terminate the currect session with the TPM by releasing the locked
+ * locality. Returns 0 on success of -1 on failure (in case lock
+ * removal did not succeed).
+ */
+int tis_close(void);
+
+/*
+ * tis_sendrecv()
+ *
+ * Send the requested data to the TPM and then try to get its response
+ *
+ * @sendbuf - buffer of the data to send
+ * @send_size size of the data to send
+ * @recvbuf - memory to save the response to
+ * @recv_len - pointer to the size of the response buffer
+ *
+ * Returns 0 on success (and places the number of response bytes at recv_len)
+ * or -1 on failure.
+ */
+int tis_sendrecv(const u8 *sendbuf, size_t send_size, u8 *recvbuf,
+			size_t *recv_len);
+
+#endif /* TPM_H_ */