diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 0fd84b9..5515afa 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -99,6 +99,8 @@ efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a
 vmlinux-objs-$(CONFIG_SECURE_LAUNCH) += $(obj)/early_sha1.o
 vmlinux-objs-$(CONFIG_SECURE_LAUNCH_SHA256) += $(obj)/early_sha256.o
 vmlinux-objs-$(CONFIG_SECURE_LAUNCH_SHA512) += $(obj)/early_sha512.o
+vmlinux-objs-$(CONFIG_SECURE_LAUNCH) += $(obj)/tpm/tpmio.o $(obj)/tpm/tpm_buff.o \
+	$(obj)/tpm/tis.o $(obj)/tpm/crb.o
 
 # The compressed kernel is built with -fPIC/-fPIE so that a boot loader
 # can place it anywhere in memory and it will still run. However, since
diff --git a/arch/x86/boot/compressed/tpm/crb.c b/arch/x86/boot/compressed/tpm/crb.c
new file mode 100644
index 0000000..05c4038
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/crb.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ */
+
+#include <linux/types.h>
+#include "tpm.h"
+#include "tpmbuff.h"
+#include "crb.h"
+#include "tpm_common.h"
+
+#define TPM_LOC_STATE		0x0000
+#define TPM_LOC_CTRL		0x0008
+#define TPM_LOC_STS		0x000C
+#define TPM_CRB_INTF_ID		0x0030
+#define TPM_CRB_CTRL_EXT	0x0038
+#define TPM_CRB_CTRL_REQ	0x0040
+#define TPM_CRB_CTRL_STS	0x0044
+#define TPM_CRB_CTRL_CANCEL	0x0048
+#define TPM_CRB_CTRL_START	0x004C
+#define TPM_CRB_INT_ENABLE	0x0050
+#define TPM_CRB_INT_STS		0x0054
+#define TPM_CRB_CTRL_CMD_SIZE	0x0058
+#define TPM_CRB_CTRL_CMD_LADDR	0x005C
+#define TPM_CRB_CTRL_CMD_HADDR	0x0060
+#define TPM_CRB_CTRL_RSP_SIZE	0x0064
+#define TPM_CRB_CTRL_RSP_ADDR	0x0068
+#define TPM_CRB_DATA_BUFFER	0x0080
+
+#define REGISTER(l, r)		(((l) << 12) | (r))
+
+static u8 locality = TPM_NO_LOCALITY;
+
+struct tpm_loc_state {
+	union {
+		u8 val;
+		struct {
+			u8 tpm_established:1;
+			u8 loc_assigned:1;
+			u8 active_locality:3;
+			u8 _reserved:2;
+			u8 tpm_reg_valid_sts:1;
+		};
+	};
+} __packed;
+
+struct tpm_loc_ctrl {
+	union {
+		u32 val;
+		struct {
+			u32 request_access:1;
+			u32 relinquish:1;
+			u32 seize:1;
+			u32 reset_establishment_bit:1;
+			u32 _reserved:28;
+		};
+	};
+} __packed;
+
+struct tpm_loc_sts {
+	union {
+		u32 val;
+		struct {
+			u32 granted:1;
+			u32 beenSeized:1;
+			u32 _reserved:30;
+		};
+	};
+} __packed;
+
+struct tpm_crb_ctrl_req {
+	union {
+		u32 val;
+		struct {
+			u32 cmd_ready:1;
+			u32 go_idle:1;
+			u32 _reserved:30;
+		};
+	};
+} __packed;
+
+struct tpm_crb_ctrl_sts {
+	union {
+		u32 val;
+		struct {
+			u32 tpm_sts:1;
+			u32 tpm_idle:1;
+			u32 _reserved:30;
+		};
+	};
+} __packed;
+
+struct tpm_crb_intf_id_ext {
+	union {
+		u32 val;
+		struct {
+			u32 vid:16;
+			u32 did:16;
+		};
+	};
+} __packed;
+
+/*
+ * Durations derived from Table 15 of the PTP but is purely an artifact of this
+ * implementation
+ */
+
+/* TPM Duration A: 20ms */
+static void duration_a(void)
+{
+	tpm_mdelay(20);
+}
+
+/* TPM Duration B: 750ms */
+static void __maybe_unused duration_b(void)
+{
+	tpm_mdelay(750);
+}
+
+/* TPM Duration C: 1000ms */
+static void __maybe_unused duration_c(void)
+{
+	tpm_mdelay(1000);
+}
+
+static u8 is_idle(void)
+{
+	struct tpm_crb_ctrl_sts ctl_sts;
+
+	ctl_sts.val = tpm_read32(REGISTER(locality, TPM_CRB_CTRL_STS));
+	if (ctl_sts.tpm_idle == 1)
+		return 1;
+
+	return 0;
+}
+
+static u8 __maybe_unused is_ready(void)
+{
+	struct tpm_crb_ctrl_sts ctl_sts;
+
+	ctl_sts.val = tpm_read32(REGISTER(locality, TPM_CRB_CTRL_STS));
+	return ctl_sts.val == 0;
+}
+
+static u8 is_cmd_exec(void)
+{
+	u32 ctrl_start;
+
+	ctrl_start = tpm_read32(REGISTER(locality, TPM_CRB_CTRL_START));
+	if (ctrl_start == 1)
+		return 1;
+
+	return 0;
+}
+
+static s8 cmd_ready(void)
+{
+	struct tpm_crb_ctrl_req ctl_req;
+
+	if (is_idle()) {
+		ctl_req.cmd_ready = 1;
+		tpm_write32(ctl_req.val, REGISTER(locality, TPM_CRB_CTRL_REQ));
+		tpm2_timeout_c();
+
+		if (is_idle())
+			return -1;
+	}
+
+	return 0;
+}
+
+static void go_idle(void)
+{
+	struct tpm_crb_ctrl_req ctl_req;
+
+	if (is_idle())
+		return;
+
+	ctl_req.go_idle = 1;
+	tpm_write32(ctl_req.val, REGISTER(locality, TPM_CRB_CTRL_REQ));
+
+	/* pause to give tpm time to complete the request */
+	tpm2_timeout_c();
+}
+
+static void crb_relinquish_locality_internal(u16 l)
+{
+	struct tpm_loc_ctrl loc_ctrl;
+
+	loc_ctrl.relinquish = 1;
+
+	tpm_write32(loc_ctrl.val, REGISTER(l, TPM_LOC_CTRL));
+}
+
+u8 crb_request_locality(u8 l)
+{
+	struct tpm_loc_state loc_state;
+	struct tpm_loc_ctrl loc_ctrl;
+	struct tpm_loc_sts loc_sts;
+
+	/* TPM_LOC_STATE is aliased across all localities */
+	loc_state.val = tpm_read8(REGISTER(0, TPM_LOC_STATE));
+
+	if (loc_state.loc_assigned == 1) {
+		if (loc_state.active_locality == l) {
+			locality = l;
+			return locality;
+		}
+
+		crb_relinquish_locality_internal(loc_state.loc_assigned);
+	}
+
+	loc_ctrl.request_access = 1;
+	tpm_write32(loc_ctrl.val, REGISTER(l, TPM_LOC_CTRL));
+
+	loc_sts.val = tpm_read32(REGISTER(l, TPM_LOC_STS));
+	if (loc_sts.granted != 1) {
+		locality = TPM_NO_LOCALITY;
+		return locality;
+	}
+
+	locality = l;
+	return locality;
+}
+
+void crb_relinquish_locality(void)
+{
+	crb_relinquish_locality_internal(locality);
+}
+
+/* assumes cancel will succeed */
+static void cancel_send(void)
+{
+	if (is_cmd_exec()) {
+		tpm_write32(1, REGISTER(locality, TPM_CRB_CTRL_CANCEL));
+		timeout_b();
+
+		tpm_write32(0, REGISTER(locality, TPM_CRB_CTRL_CANCEL));
+	}
+}
+
+size_t crb_send(struct tpmbuff *buf)
+{
+	u32 ctrl_start = 1;
+
+	if (is_idle())
+		return 0;
+
+	tpm_write32(ctrl_start, REGISTER(locality, TPM_CRB_CTRL_START));
+
+	/*
+	 * Most command sequences this code is interested with operates with
+	 * 20/750 duration/timeout schedule
+	 */
+	duration_a();
+	ctrl_start = tpm_read32(REGISTER(locality, TPM_CRB_CTRL_START));
+	if (ctrl_start != 0) {
+		timeout_a();
+		ctrl_start = tpm_read32(REGISTER(locality, TPM_CRB_CTRL_START));
+		if (ctrl_start != 0) {
+			cancel_send();
+			/* minimum response is header with cancel ord */
+			return sizeof(struct tpm_header);
+		}
+	}
+
+	return buf->len;
+}
+
+size_t crb_recv(__attribute__((unused)) enum tpm_family family,
+		__attribute__((unused)) struct tpmbuff *buf)
+{
+	/* noop, currently send waits until execution is complete*/
+	return 0;
+}
+
+u8 crb_init(struct tpm *t)
+{
+	u8 i;
+	struct tpm_crb_intf_id_ext id;
+
+	if (crb_request_locality(0) == TPM_NO_LOCALITY)
+		return 0;
+
+	id.val = tpm_read32(REGISTER(0, TPM_CRB_INTF_ID + 4));
+	t->vendor = ((id.vid & 0x00FF) << 8) | ((id.vid & 0xFF00) >> 8);
+	if ((t->vendor & 0xFFFF) == 0xFFFF)
+		return 0;
+
+	/* have the tpm invalidate the buffer if left in completion state */
+	go_idle();
+	/* now move to ready state */
+	cmd_ready();
+
+	t->ops.request_locality = crb_request_locality;
+	t->ops.relinquish_locality = crb_relinquish_locality;
+	t->ops.send = crb_send;
+	t->ops.recv = crb_recv;
+
+	return 1;
+}
diff --git a/arch/x86/boot/compressed/tpm/crb.h b/arch/x86/boot/compressed/tpm/crb.h
new file mode 100644
index 0000000..8ab162e
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/crb.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ *
+ * The definitions in this header are extracted from the Trusted Computing
+ * Group's "TPM Main Specification", Parts 1-3.
+ *
+ */
+
+#ifndef _CRB_H
+#define _CRB_H
+
+#include "tpm.h"
+
+u8 crb_init(struct tpm *t);
+
+#endif
diff --git a/arch/x86/boot/compressed/tpm/tis.c b/arch/x86/boot/compressed/tpm/tis.c
new file mode 100644
index 0000000..bc67b23
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/tis.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ *
+ * The code in this file is based on the article "Writing a TPM Device Driver"
+ * published on http://ptgmedia.pearsoncmg.com.
+ *
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include "tpm.h"
+#include "tpmbuff.h"
+#include "tpm_common.h"
+#include "tis.h"
+
+#define TPM_BURST_MIN_DELAY 100 /* 100us */
+
+static u8 locality = TPM_NO_LOCALITY;
+
+static u32 burst_wait(void)
+{
+	u32 count = 0;
+
+	while (count == 0) {
+		count = tpm_read8(STS(locality) + 1);
+		count += tpm_read8(STS(locality) + 2) << 8;
+
+		/* Wait for FIFO to drain */
+		if (count == 0)
+			tpm_udelay(TPM_BURST_MIN_DELAY);
+	}
+
+	return count;
+}
+
+void tis_relinquish_locality(void)
+{
+	if (locality < TPM_MAX_LOCALITY)
+		tpm_write8(ACCESS_RELINQUISH_LOCALITY, ACCESS(locality));
+
+	locality = TPM_NO_LOCALITY;
+}
+
+u8 tis_request_locality(u8 l)
+{
+	if (l > TPM_MAX_LOCALITY)
+		return TPM_NO_LOCALITY;
+
+	if (l == locality)
+		return locality;
+
+	tis_relinquish_locality();
+
+	tpm_write8(ACCESS_REQUEST_USE, ACCESS(l));
+
+	/* wait for locality to be granted */
+	if (tpm_read8(ACCESS(l)) & ACCESS_ACTIVE_LOCALITY)
+		locality = l;
+
+	return locality;
+}
+
+size_t tis_send(struct tpmbuff *buf)
+{
+	u8 status, *buf_ptr;
+	u32 burstcnt = 0;
+	u32 count = 0;
+
+	if (locality > TPM_MAX_LOCALITY)
+		return 0;
+
+	for (status = 0; (status & STS_COMMAND_READY) == 0; ) {
+		tpm_write8(STS_COMMAND_READY, STS(locality));
+		status = tpm_read8(STS(locality));
+	}
+
+	buf_ptr = buf->head;
+
+	/* send all but the last byte */
+	while (count < (buf->len - 1)) {
+		burstcnt = burst_wait();
+		for (; burstcnt > 0 && count < (buf->len - 1); burstcnt--) {
+			tpm_write8(buf_ptr[count], DATA_FIFO(locality));
+			count++;
+		}
+
+		/* check for overflow */
+		for (status = 0; (status & STS_VALID) == 0; )
+			status = tpm_read8(STS(locality));
+
+		if ((status & STS_DATA_EXPECT) == 0)
+			return 0;
+	}
+
+	/* write last byte */
+	tpm_write8(buf_ptr[buf->len - 1], DATA_FIFO(locality));
+	count++;
+
+	/* make sure it stuck */
+	for (status = 0; (status & STS_VALID) == 0; )
+		status = tpm_read8(STS(locality));
+
+	if ((status & STS_DATA_EXPECT) != 0)
+		return 0;
+
+	/* go and do it */
+	tpm_write8(STS_GO, STS(locality));
+
+	return (size_t)count;
+}
+
+static size_t recv_data(unsigned char *buf, size_t len)
+{
+	size_t size = 0;
+	u8 *bufptr;
+	u32 burstcnt = 0;
+
+	bufptr = (u8 *)buf;
+
+	while (tis_data_available(locality) && size < len) {
+		burstcnt = burst_wait();
+		for (; burstcnt > 0 && size < len; burstcnt--) {
+			*bufptr = tpm_read8(DATA_FIFO(locality));
+			bufptr++;
+			size++;
+		}
+	}
+
+	return size;
+}
+
+size_t tis_recv(enum tpm_family f, struct tpmbuff *buf)
+{
+	u32 expected;
+	u8 *buf_ptr;
+	struct tpm_header *hdr;
+
+	if (locality > TPM_MAX_LOCALITY)
+		return 0;
+
+	/* ensure that there is data available */
+	if (!tis_data_available(locality)) {
+		if (f == TPM12)
+			tpm1_timeout_d();
+		else
+			tpm2_timeout_d();
+
+		if (!tis_data_available(locality))
+			return 0;
+	}
+
+	/* read header */
+	hdr = (struct tpm_header *)buf->head;
+	expected = sizeof(struct tpm_header);
+	if (recv_data(buf->head, expected) < expected)
+		return 0;
+
+	/* convert header */
+	hdr->tag = be16_to_cpu(hdr->tag);
+	hdr->size = be32_to_cpu(hdr->size);
+	hdr->code = be32_to_cpu(hdr->code);
+
+	/* protect against integer underflow */
+	if (hdr->size <= expected)
+		return 0;
+
+	/* hdr->size = header + data */
+	expected = hdr->size - expected;
+	buf_ptr = tpmb_put(buf, expected);
+	if (!buf_ptr)
+		return 0;
+
+	/* read all data, except last byte */
+	if (recv_data(buf_ptr, expected - 1) < (expected - 1))
+		return 0;
+
+	/* check for receive underflow */
+	if (!tis_data_available(locality))
+		return 0;
+
+	/* read last byte */
+	if (recv_data(buf_ptr, 1) != 1)
+		return 0;
+
+	/* make sure we read everything */
+	if (tis_data_available(locality))
+		return 0;
+
+	tpm_write8(STS_COMMAND_READY, STS(locality));
+
+	return hdr->size;
+}
+
+u8 tis_init(struct tpm *t)
+{
+	locality = TPM_NO_LOCALITY;
+
+	if (tis_request_locality(0) != 0)
+		return 0;
+
+	t->vendor = tpm_read32(DID_VID(0));
+	if ((t->vendor & 0xFFFF) == 0xFFFF)
+		return 0;
+
+	t->ops.request_locality = tis_request_locality;
+	t->ops.relinquish_locality = tis_relinquish_locality;
+	t->ops.send = tis_send;
+	t->ops.recv = tis_recv;
+
+	return 1;
+}
diff --git a/arch/x86/boot/compressed/tpm/tis.h b/arch/x86/boot/compressed/tpm/tis.h
new file mode 100644
index 0000000..31e6df1
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/tis.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ *
+ * The definitions in this header are extracted from the Trusted Computing
+ * Group's "TPM Main Specification", Parts 1-3.
+ *
+ */
+
+#ifndef _TIS_H
+#define _TIS_H
+
+#include "tpm.h"
+#include "tpm_common.h"
+
+/* macros to access registers at locality ’’l’’ */
+#define ACCESS(l)			(0x0000 | ((l) << 12))
+#define STS(l)				(0x0018 | ((l) << 12))
+#define DATA_FIFO(l)			(0x0024 | ((l) << 12))
+#define DID_VID(l)			(0x0F00 | ((l) << 12))
+/* access bits */
+#define ACCESS_ACTIVE_LOCALITY		0x20 /* (R)*/
+#define ACCESS_RELINQUISH_LOCALITY	0x20 /* (W) */
+#define ACCESS_REQUEST_USE		0x02 /* (W) */
+/* status bits */
+#define STS_VALID			0x80 /* (R) */
+#define STS_COMMAND_READY		0x40 /* (R) */
+#define STS_DATA_AVAIL			0x10 /* (R) */
+#define STS_DATA_EXPECT			0x08 /* (R) */
+#define STS_GO				0x20 /* (W) */
+
+static inline bool tis_data_available(int locality)
+{
+	int status;
+
+	status = tpm_read8(STS(locality));
+	return ((status & (STS_DATA_AVAIL | STS_VALID)) ==
+		(STS_DATA_AVAIL | STS_VALID));
+}
+
+u8 tis_init(struct tpm *t);
+
+#endif
diff --git a/arch/x86/boot/compressed/tpm/tpm.h b/arch/x86/boot/compressed/tpm/tpm.h
new file mode 100644
index 0000000..1d3f12f
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/tpm.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ *
+ */
+
+#ifndef _TPM_H
+#define _TPM_H
+
+#define TPM_NO_LOCALITY		0xFF
+
+enum tpm_hw_intf {
+	TPM_TIS,
+	TPM_CRB
+};
+
+enum tpm_family {
+	TPM12,
+	TPM20
+};
+
+struct tpmbuff;
+
+struct tpm_hw_ops {
+	u8 (*request_locality)(u8 l);
+	void (*relinquish_locality)(void);
+	size_t (*send)(struct tpmbuff *buf);
+	size_t (*recv)(enum tpm_family family, struct tpmbuff *buf);
+};
+
+struct tpm {
+	u32 vendor;
+	enum tpm_family family;
+	enum tpm_hw_intf intf;
+	struct tpm_hw_ops ops;
+	struct tpmbuff *buff;
+};
+
+extern struct tpm *enable_tpm(void);
+extern u8 tpm_request_locality(struct tpm *t, u8 l);
+extern void tpm_relinquish_locality(struct tpm *t);
+extern int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo,
+		u8 *digest);
+extern void free_tpm(struct tpm *t);
+#endif
diff --git a/arch/x86/boot/compressed/tpm/tpm_buff.c b/arch/x86/boot/compressed/tpm/tpm_buff.c
new file mode 100644
index 0000000..628ac87
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/tpm_buff.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include "tpm.h"
+#include "tpmbuff.h"
+#include "tpm_common.h"
+
+#define STATIC_TIS_BUFFER_SIZE		1024
+
+#define TPM_CRB_DATA_BUFFER_OFFSET	0x80
+#define TPM_CRB_DATA_BUFFER_SIZE	3966
+
+u8 *tpmb_reserve(struct tpmbuff *b)
+{
+	if (b->locked)
+		return NULL;
+
+	b->len = sizeof(struct tpm_header);
+	b->locked = 1;
+	b->data = b->head + b->len;
+	b->tail = b->data;
+
+	return b->head;
+}
+
+void tpmb_free(struct tpmbuff *b)
+{
+	memset(b->head, 0, b->len);
+
+	b->len = 0;
+	b->locked = 0;
+	b->data = NULL;
+	b->tail = NULL;
+}
+
+u8 *tpmb_put(struct tpmbuff *b, size_t size)
+{
+	u8 *tail = b->tail;
+
+	if ((b->len + size) > b->truesize)
+		return NULL; /* TODO: add overflow buffer support */
+
+	b->tail += size;
+	b->len += size;
+
+	return tail;
+}
+
+size_t tpmb_trim(struct tpmbuff *b, size_t size)
+{
+	if (b->len < size)
+		size = b->len;
+
+	/* TODO: add overflow buffer support */
+
+	b->tail -= size;
+	b->len -= size;
+
+	return size;
+}
+
+size_t tpmb_size(struct tpmbuff *b)
+{
+	return b->len;
+}
+
+static u8 tis_buff[STATIC_TIS_BUFFER_SIZE];
+static struct tpmbuff tpm_buff;
+
+struct tpmbuff *alloc_tpmbuff(enum tpm_hw_intf intf, u8 locality)
+{
+	struct tpmbuff *b = &tpm_buff;
+
+	switch (intf) {
+	case TPM_TIS:
+		if (b->head)
+			goto reset;
+
+		b->head = (u8 *)&tis_buff;
+		b->truesize = STATIC_TIS_BUFFER_SIZE;
+		break;
+	case TPM_CRB:
+		b->head = (u8 *)(uintptr_t)(TPM_MMIO_BASE + (locality << 12)
+			       + TPM_CRB_DATA_BUFFER_OFFSET);
+		b->truesize = TPM_CRB_DATA_BUFFER_SIZE;
+		break;
+	default:
+		return NULL;
+	}
+
+reset:
+	b->len = 0;
+	b->locked = 0;
+	b->data = NULL;
+	b->tail = NULL;
+	b->end = b->head + (b->truesize - 1);
+
+	return b;
+}
+
+void free_tpmbuff(struct tpmbuff *b, enum tpm_hw_intf intf)
+{
+	switch (intf) {
+	case TPM_TIS:
+		b->head = NULL;
+		break;
+	case TPM_CRB:
+		b->head = NULL;
+		break;
+	default:
+		break;
+	}
+}
diff --git a/arch/x86/boot/compressed/tpm/tpm_common.h b/arch/x86/boot/compressed/tpm/tpm_common.h
new file mode 100644
index 0000000..84f765c
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/tpm_common.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ *
+ */
+
+#ifndef _TPM_COMMON_H
+#define _TPM_COMMON_H
+
+#define TPM_MMIO_BASE		0xFED40000
+#define TPM_MAX_LOCALITY	4
+
+#define SHA1_SIZE	20
+#define SHA256_SIZE	32
+#define SHA384_SIZE	48
+#define SHA512_SIZE	64
+#define SM3256_SIZE	32
+
+struct tpm_header {
+	u16 tag;
+	u32 size;
+	u32 code;
+} __packed;
+
+#define TPM_INTERFACE_ID_0	0x30
+#define TPM_TIS_INTF_ACTIVE	0x00
+#define TPM_CRB_INTF_ACTIVE	0x01
+
+struct tpm_interface_id {
+	union {
+		u32 val;
+		struct {
+			u32 interface_type:4;
+			u32 interface_version:4;
+			u32 cap_locality:1;
+			u32 reserved1:4;
+			u32 cap_tis:1;
+			u32 cap_crb:1;
+			u32 cap_if_res:2;
+			u32 interface_selector:2;
+			u32 intf_sel_lock:1;
+			u32 reserved2:4;
+			u32 reserved3:8;
+		};
+	};
+} __packed;
+
+#define TPM_INTF_CAPABILITY_0	0x14
+#define TPM12_TIS_INTF_12	0x00
+#define TPM12_TIS_INTF_13	0x02
+#define TPM20_TIS_INTF_13	0x03
+
+struct tpm_intf_capability {
+	union {
+		u32 val;
+		struct {
+			u32 data_avail_int_support:1;
+			u32 sts_valid_int_support:1;
+			u32 locality_change_int_support:1;
+			u32 interrupt_level_high:1;
+			u32 interrupt_level_low:1;
+			u32 interrupt_edge_rising:1;
+			u32 interrupt_edge_falling:1;
+			u32 command_ready_int_support:1;
+			u32 burst_count_static:1;
+			u32 data_transfer_size_support:2;
+			u32 reserved1:17;
+			u32 interface_version:3;
+			u32 reserved2:1;
+		};
+	};
+} __packed;
+
+void tpm_udelay(int loops);
+void tpm_mdelay(int ms);
+
+/*
+ * Timeouts defined in Table 16 from the TPM2 PTP and
+ * Table 15 from the PC Client TIS
+ */
+
+/* TPM Timeout A: 750ms */
+static inline void timeout_a(void)
+{
+	tpm_mdelay(750);
+}
+
+/* TPM Timeout B: 2000ms */
+static inline void timeout_b(void)
+{
+	tpm_mdelay(2000);
+}
+
+/* Timeouts C & D are different between 1.2 & 2.0 */
+/* TPM1.2 Timeout C: 750ms */
+static inline void tpm1_timeout_c(void)
+{
+	tpm_mdelay(750);
+}
+
+/* TPM1.2 Timeout D: 750ms */
+static inline void tpm1_timeout_d(void)
+{
+	tpm_mdelay(750);
+}
+
+/* TPM2 Timeout C: 200ms */
+static inline void tpm2_timeout_c(void)
+{
+	tpm_mdelay(200);
+}
+
+/* TPM2 Timeout D: 30ms */
+static inline void tpm2_timeout_d(void)
+{
+	tpm_mdelay(30);
+}
+
+u8 tpm_read8(u32 field);
+void tpm_write8(unsigned char val, u32 field);
+u32 tpm_read32(u32 field);
+void tpm_write32(unsigned int val, u32 field);
+
+#endif
diff --git a/arch/x86/boot/compressed/tpm/tpmbuff.h b/arch/x86/boot/compressed/tpm/tpmbuff.h
new file mode 100644
index 0000000..562e586
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/tpmbuff.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ *
+ */
+
+#ifndef _TPMBUFF_H
+#define _TPMBUFF_H
+
+/* mirroring Linux SKB */
+struct tpmbuff {
+	size_t truesize;
+	size_t len;
+
+	u8 locked;
+
+	u8 *head;
+	u8 *data;
+	u8 *tail;
+	u8 *end;
+};
+
+u8 *tpmb_reserve(struct tpmbuff *b);
+void tpmb_free(struct tpmbuff *b);
+u8 *tpmb_put(struct tpmbuff *b, size_t size);
+size_t tpmb_trim(struct tpmbuff *b, size_t size);
+size_t tpmb_size(struct tpmbuff *b);
+struct tpmbuff *alloc_tpmbuff(enum tpm_hw_intf i, u8 locality);
+void free_tpmbuff(struct tpmbuff *b, enum tpm_hw_intf i);
+
+#endif
diff --git a/arch/x86/boot/compressed/tpm/tpmio.c b/arch/x86/boot/compressed/tpm/tpmio.c
new file mode 100644
index 0000000..1502ec1
--- /dev/null
+++ b/arch/x86/boot/compressed/tpm/tpmio.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@apertussolutions.com>
+ */
+
+#include <linux/types.h>
+#include <asm/io.h>
+#include "tpm_common.h"
+
+static noinline void tpm_io_delay(void)
+{
+	/* This is the default delay type in native_io_delay */
+	asm volatile ("outb %al, $0x80");
+}
+
+void tpm_udelay(int loops)
+{
+	while (loops--)
+		tpm_io_delay();	/* Approximately 1 us */
+}
+
+void tpm_mdelay(int ms)
+{
+	int i;
+
+	for (i = 0; i < ms; i++)
+		tpm_udelay(1000);
+}
+
+u8 tpm_read8(u32 field)
+{
+	return readb((void *)(u64)(TPM_MMIO_BASE | field));
+}
+
+void tpm_write8(unsigned char val, u32 field)
+{
+	writeb(val, (void *)(u64)(TPM_MMIO_BASE | field));
+}
+
+u32 tpm_read32(u32 field)
+{
+	return readl((void *)(u64)(TPM_MMIO_BASE | field));
+}
+
+void tpm_write32(u32 val, u32 field)
+{
+	writel(val, (void *)(u64)(TPM_MMIO_BASE | field));
+}
