summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2015-07-21 13:54:00 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-07-25 08:47:37 +0000
commit15135076e20dc5168a7e61ffe90967cf162e5767 (patch)
tree78a34bf810f9e08c7adf386ad442c0b5ae054658
parent3a77fd73327cf532eedbe92fafd3a61598d2dc53 (diff)
downloadchrome-ec-15135076e20dc5168a7e61ffe90967cf162e5767.tar.gz
Cr50: Enable TPM-protocol data over the SPI bus
This patch adds a module which runs on top of the SPS driver and implements the TCG SPI TPM protocol. Basic register read and write functions are implemented as well as rudimentary TPM state machine (claiming/releasing locality). An enhancement is made to the SPS driver to ensure that when the CS is deasserted the transmit FIFO is reset too, on the off chance of the CS going away mid transaction for whatever reason. In this implementation the slave is guaranteed to stall the master for a few bytes in both receive and transmit transactions, which is further aggravated by the fact that RX FIFO threshold is set to 8 (this is the minimum number of bytes the master has to send to wake up the slave). This could be fine tuned later, for instance made a parameter of the receive callback registration function. BRANCH=none BUG=chrome-os-partner:43025 TEST=trunksd initialization (with minor changes to accommodate new VID/DID and some status bits, to be published) succeeds with the cr50 connected to the USB/SPI cable. Change-Id: I28d37c3b57dde9adf59e81426efe4f58880cf0b0 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/285252
-rw-r--r--board/cr50/board.h1
-rw-r--r--chip/g/build.mk1
-rw-r--r--chip/g/sps.c8
-rw-r--r--chip/g/sps.h6
-rw-r--r--chip/g/sps_tpm.c282
-rw-r--r--common/build.mk1
-rw-r--r--common/tpm_registers.c166
-rw-r--r--include/config.h6
-rw-r--r--include/tpm_registers.h24
9 files changed, 492 insertions, 3 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 5cd5c0f621..3d94b4cb5d 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -27,6 +27,7 @@
/* Enable SPI Slave (SPS) module */
#define CONFIG_SPS
#define CONFIG_HOSTCMD_SPS
+#define CONFIG_TPM_SPS
/* We don't need to send events to the AP */
#undef CONFIG_HOSTCMD_EVENTS
diff --git a/chip/g/build.mk b/chip/g/build.mk
index a9747d3aea..5157dee9a5 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -20,6 +20,7 @@ chip-y=clock.o gpio.o hwtimer.o jtag.o system.o uart.o
chip-y+= pmu.o
chip-$(CONFIG_SPS)+= sps.o
chip-$(CONFIG_HOSTCMD_SPS)+=sps_hc.o
+chip-$(CONFIG_TPM_SPS)+=sps_tpm.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
diff --git a/chip/g/sps.c b/chip/g/sps.c
index 837d3c6884..82690b01ba 100644
--- a/chip/g/sps.c
+++ b/chip/g/sps.c
@@ -41,8 +41,6 @@
* - unregister receive callback.
*/
-#define SPS_FIFO_SIZE (1 << 10)
-#define SPS_FIFO_MASK (SPS_FIFO_SIZE - 1)
/*
* Hardware pointers use one extra bit, which means that indexing FIFO and
* values written into the pointers have to have dfferent sizes. Tracked under
@@ -321,6 +319,12 @@ static void sps_cs_deassert_interrupt(uint32_t port)
sps_rx_interrupt(port, 1);
GWRITE_FIELD(SPS, ISTATE_CLR, CS_DEASSERT, 1);
GWRITE_FIELD(SPS, FIFO_CTRL, TXFIFO_EN, 0);
+
+ /*
+ * And transmit FIFO is emptied, so the next transaction doesn't start
+ * by clocking out any bytes left over from this one.
+ */
+ GREG32(SPS, TXFIFO_WPTR) = GREG32(SPS, TXFIFO_RPTR);
}
void _sps0_interrupt(void)
diff --git a/chip/g/sps.h b/chip/g/sps.h
index 52f47c4020..83505909f1 100644
--- a/chip/g/sps.h
+++ b/chip/g/sps.h
@@ -18,13 +18,17 @@ enum sps_mode {
SPS_UNDEF_MODE = 3,
};
+/* Receive and transmit FIFO size and mask. */
+#define SPS_FIFO_SIZE (1 << 10)
+#define SPS_FIFO_MASK (SPS_FIFO_SIZE - 1)
+
/*
* Tx interrupt callback function prototype. This function returns a portion
* of the received SPI data and current status of the CS line. When CS is
* deasserted, this function is called with data_size of zero and a non-zero
* cs_status. This allows the recipient to delineate the SPS frames.
*/
-typedef void (*rx_handler_f)(uint8_t *data, size_t data_size, int cs_status);
+typedef void (*rx_handler_f)(uint8_t *data, size_t data_size, int cs_disabled);
/*
* Push data to the SPS TX FIFO
diff --git a/chip/g/sps_tpm.c b/chip/g/sps_tpm.c
new file mode 100644
index 0000000000..5ce79a661f
--- /dev/null
+++ b/chip/g/sps_tpm.c
@@ -0,0 +1,282 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "sps.h"
+#include "tpm_registers.h"
+#include "util.h"
+
+/*
+ * This implements the TCG's TPM SPI Hardware Protocol on the SPI bus, using
+ * the Cr50 SPS (SPI slave) controller. This turns out to be very similar to
+ * the EC host command protocol, which is itself similar to HDLC. All of those
+ * protocols provide ways to identify data frames over transports that don't
+ * provide them natively. That's the nice thing about standards: there are so
+ * many to choose from.
+ *
+ * ANYWAY, The goal of the TPM protocol is to provide read and write access to
+ * device registers over the SPI bus. It is defined as follows (note that
+ * master clocks the bus and master and slave tramsmit data simultaneously).
+ *
+ * Each transaction starts with the master clocking the bus to transfer 4
+ * bytes:
+ *
+ * The master sends 4 bytes: [R/W+size-1] [Addr] [Addr] [Addr]
+ * The slave also sends 4 bytes: [xx] [xx] [xx] [x?]
+ *
+ * Bytes sent by the master define the direction and size (1-64 bytes) of the
+ * data transfer, and the address of the register to access.
+ *
+ * The final bit of the 4th slave response byte determines whether or not the
+ * slave needs some extra time. If that bit is 1, the master can IMMEDIATELY
+ * clock in (or out) the number of bytes it specified with the header byte 0.
+ *
+ * If the final bit of the 4th response byte is 0, the master clocks eight
+ * more bits and looks again at the new received byte. It repeats this (clock
+ * 8 bits, look at last bit) as long as every eighth bit is 0.
+ *
+ * When the slave is ready to proceed with the data transfer, it returns a 1
+ * for the final bit of the response byte, at which point the master has to
+ * resume transferring valid data for write transactions or to start deading
+ * bytes sent by the slave in case of read transactions.
+ *
+ * So here's what a 4-byte write of value of 0x11223344 to register 0xAABBCC
+ * might look like:
+ *
+ * xfer: 1 2 3 4 5 6 7 8 9 10 11
+ * MOSI: 03 aa bb cc xx xx xx 11 22 33 44
+ * MISO: xx xx xx x0 x0 x0 x1 xx xx xx xx
+ *
+ * Bit 0 of MISO xfer #4 is 0, indicating that the slave needs to stall. The
+ * slave stalled for three bytes before it was ready to continue accepting the
+ * input data from the master. The slave released the stall in xfer #7.
+ *
+ * Here's a 4-byte read from register 0xAABBCC:
+ *
+ * xfer: 1 2 3 4 5 6 7 8 9 10 11
+ * MOSI: 83 aa bb cc xx xx xx xx xx xx xx
+ * MISO: xx xx xx x0 x0 x0 x1 11 22 33 44
+ *
+ * As before, the slave stalled the read for three bytes and indicated it was
+ * done stalling at xfer #7.
+ *
+ * Note that the ONLY place where a stall can be initiated is the last bit of
+ * the fourth MISO byte of the transaction. Once the stall is released,
+ * there's no stopping the rest of the data transfer.
+ */
+
+#define TPM_STALL_ASSERT 0x00
+#define TPM_STALL_DEASSERT 0x01
+
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_TPM, outstr)
+#define CPRINTS(format, args...) cprints(CC_TPM, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_TPM, format, ## args)
+
+/*
+ * Incoming messages are collected here until they're ready to process. The
+ * buffer will start with a four-byte header, followed by whatever data
+ * is sent by the master (none for a read, 1 to 64 bytes for a write).
+ */
+#define RXBUF_MAX 512 /* chosen arbitrarily */
+static uint8_t rxbuf[RXBUF_MAX];
+static unsigned rxbuf_count; /* num bytes received */
+static unsigned rxbuf_needed; /* num bytes we'd like */
+static unsigned rx_fifo_base; /* RX fifo at transaction start. */
+static unsigned stall_threshold; /* num bytes we'd like */
+static uint32_t bytecount;
+static uint32_t regaddr;
+
+
+/*
+ * Outgoing messages are shoved in here. We need a TPM_STALL_DEASSERT byte to
+ * mark the start of the data stream before the data itself.
+ */
+#define TXBUF_MAX 512 /* chosen arbitrarily */
+static uint8_t txbuf[1 + TXBUF_MAX];
+
+static enum sps_state {
+ /* Receiving header */
+ SPS_TPM_STATE_RECEIVING_HEADER,
+
+ /* Receiving data. */
+ SPS_TPM_STATE_RECEIVING_WRITE_DATA,
+
+ /* Finished rx processing, waiting for SPI transaction to finish. */
+ SPS_TPM_STATE_PONDERING,
+
+ /* Something went wrong. */
+ SPS_TPM_STATE_RX_BAD,
+} sps_tpm_state;
+
+/* Set initial conditions to get ready to receive a command. */
+static void init_new_cycle(void)
+{
+ rxbuf_count = 0;
+ rxbuf_needed = 4;
+ sps_tpm_state = SPS_TPM_STATE_RECEIVING_HEADER;
+ rx_fifo_base = sps_rx_fifo_wrptr();
+ sps_tx_status(TPM_STALL_ASSERT);
+}
+
+/* Extract R/W bit, register addresss, and data count from 4-byte header */
+static int header_says_to_read(uint8_t *data, uint32_t *reg, uint32_t *count)
+{
+ uint32_t addr = data[1]; /* reg address is MSB first */
+ addr = (addr << 8) + data[2];
+ addr = (addr << 8) + data[3];
+ *reg = addr;
+ *count = (data[0] & 0x3f) + 1; /* bits 5-0: 1 to 64 bytes */
+ return !!(data[0] & 0x80); /* bit 7: 1=read, 0=write */
+}
+
+/* actual RX FIFO handler (runs in interrupt context) */
+static void process_rx_data(uint8_t *data, size_t data_size)
+{
+ /* We're collecting incoming bytes ... */
+ if ((rxbuf_count + data_size) > RXBUF_MAX) {
+ CPRINTS("TPM SPI input overflow: %d + %d > %d in state %d",
+ rxbuf_count, data_size, RXBUF_MAX, sps_tpm_state);
+ sps_tx_status(TPM_STALL_DEASSERT);
+ sps_tpm_state = SPS_TPM_STATE_RX_BAD;
+ return;
+ }
+ memcpy(rxbuf + rxbuf_count, data, data_size);
+ rxbuf_count += data_size;
+
+ /* Wait until we have enough. */
+ if (rxbuf_count < rxbuf_needed)
+ return;
+
+ /* Okay, we have enough. Now what? */
+ if (sps_tpm_state == SPS_TPM_STATE_RECEIVING_HEADER) {
+ uint32_t old_wrptr, wrptr;
+
+ /* Got the header. What's it say to do? */
+ if (header_says_to_read(rxbuf, &regaddr, &bytecount)) {
+ /* Send the stall deassert manually */
+ txbuf[0] = TPM_STALL_DEASSERT;
+
+ /* Copy the register contents into the TXFIFO */
+ /* TODO: This is blindly assuming TXFIFO has enough
+ * room. What can we do if it doesn't? */
+ tpm_register_get(regaddr, txbuf + 1, bytecount);
+ sps_transmit(txbuf, bytecount + 1);
+ sps_tpm_state = SPS_TPM_STATE_PONDERING;
+ return;
+ }
+
+ /*
+ * Master is writing, we will need more data.
+ *
+ * This is a tricky part, as we do not know how many dummy
+ * bytes the master has already written. And the actual data
+ * of course will start arriving only after we change the idle
+ * byte to set the LSB to 1.
+ *
+ * What we do know is that the idle byte repeatedly sent on
+ * the MISO line is sampled at the same time as the RX FIFO
+ * write pointer is written by the chip, after clocking in the
+ * next byte. That is, we can synchronize with the line by
+ * waiting for the RX FIFO write pointer to change. Then we
+ * can change the idle byte to indicate that the slave is
+ * ready to receive the rest of the data, and take note of the
+ * RX FIFO write pointer, as the first byte of the message
+ * will show up in the receive FIFO 2 bytes later.
+ */
+
+ /*
+ * Let's wait til the start of the next byte cycle. This must
+ * be done in a tight loop (with interrupts disabled?).
+ */
+ old_wrptr = sps_rx_fifo_wrptr();
+ do {
+ wrptr = sps_rx_fifo_wrptr();
+ } while (old_wrptr == wrptr);
+
+ /*
+ * Write the new idle byte value, it will start transmitting
+ * *next* after the current byte.
+ */
+ sps_tx_status(TPM_STALL_DEASSERT);
+
+ /*
+ * Verify that we managed to change the idle byte value within
+ * the required time (RX FIFO write pointer has not changed)
+ */
+ if (sps_rx_fifo_wrptr() != wrptr) {
+ CPRINTS("%s:"
+ " ERROR: failed to change idle byte in time",
+ __func__);
+ sps_tpm_state = SPS_TPM_STATE_PONDERING;
+ } else {
+ /*
+ * Ok, we're good. Remember where in the receive
+ * stream the actual data will start showing up. It is
+ * two bytes after the current one (the current idle
+ * byte still has the LSB set to zero, the next one
+ * will have the LSB set to one, only after receiving
+ * it the master will start sending the actual data.
+ */
+ stall_threshold =
+ ((wrptr - rx_fifo_base) & SPS_FIFO_MASK) + 2;
+ rxbuf_needed = stall_threshold + bytecount;
+ sps_tpm_state = SPS_TPM_STATE_RECEIVING_WRITE_DATA;
+ }
+ return;
+ }
+
+ if (sps_tpm_state == SPS_TPM_STATE_RECEIVING_WRITE_DATA) {
+ /* Ok, we have all the write data. */
+ tpm_register_put(regaddr, rxbuf + stall_threshold, bytecount);
+ sps_tpm_state = SPS_TPM_STATE_PONDERING;
+ }
+}
+
+static void tpm_rx_handler(uint8_t *data, size_t data_size, int cs_disabled)
+{
+ if ((sps_tpm_state == SPS_TPM_STATE_RECEIVING_HEADER) ||
+ (sps_tpm_state == SPS_TPM_STATE_RECEIVING_WRITE_DATA))
+ process_rx_data(data, data_size);
+
+ if (cs_disabled)
+ init_new_cycle();
+}
+
+static void sps_tpm_enable(void)
+{
+ sps_register_rx_handler(SPS_GENERIC_MODE, tpm_rx_handler);
+ init_new_cycle();
+}
+
+static void sps_tpm_disable(void)
+{
+ sps_tpm_state = SPS_TPM_STATE_PONDERING;
+ sps_unregister_rx_handler();
+}
+
+static int command_sps_tpm(int argc, char **argv)
+{
+ if (argc > 1) {
+ if (0 != strcasecmp(argv[1], "off"))
+ return EC_ERROR_PARAM1;
+
+ sps_tpm_disable();
+ ccprintf("TPM SPI protocol disabled\n");
+ return EC_SUCCESS;
+ }
+
+ sps_tpm_enable();
+ ccprintf("TPM SPI protocol enabled\n");
+ return EC_SUCCESS;
+}
+
+DECLARE_CONSOLE_COMMAND(spstpm, command_sps_tpm,
+ "[off]",
+ "Not sure yet...",
+ NULL);
diff --git a/common/build.mk b/common/build.mk
index 32012e400c..2fb599ea06 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -81,6 +81,7 @@ common-$(CONFIG_SPI_FLASH)+=spi_flash.o spi_flash_reg.o
common-$(CONFIG_SWITCH)+=switch.o
common-$(CONFIG_SW_CRC)+=crc.o
common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o thermal.o throttle_ap.o
+common-$(CONFIG_TPM_SPS)+=tpm_registers.o
common-$(CONFIG_USB_CHARGER)+=usb_charger.o
common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o
common-$(CONFIG_USB_PORT_POWER_SMART)+=usb_port_power_smart.o
diff --git a/common/tpm_registers.c b/common/tpm_registers.c
new file mode 100644
index 0000000000..a3f1672b70
--- /dev/null
+++ b/common/tpm_registers.c
@@ -0,0 +1,166 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This implements the register interface for the TPM SPI Hardware Protocol.
+ * The master puts or gets between 1 and 64 bytes to a register designated by a
+ * 24-bit address. There is no provision for error reporting at this level.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "tpm_registers.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_TPM, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_TPM, format, ## args)
+
+
+/* Register addresses for FIFO mode. */
+#define TPM_ACCESS 0
+#define TPM_INT_ENABLE 8
+#define TPM_INT_VECTOR 0xC
+#define TPM_INT_STATUS 0x10
+#define TPM_INTF_CAPABILITY 0x14
+#define TPM_STS 0x18
+#define TPM_DATA_FIFO 0x28
+#define TPM_INTERFACE_ID 0x30
+#define TPM_DID_VID 0xf00
+#define TPM_RID 0xf04
+
+#define GOOGLE_VID 0x1ae0
+#define GOOGLE_DID 0x0028
+#define CR50_RID 0 /* No revision ID yet */
+
+/* A preliminary interface capability register value, will be fine tuned. */
+#define IF_CAPABILITY_REG ((3 << 28) | /* TPM2.0 (interface 1.3) */ \
+ (3 << 9) | /* up to 64 bytes transfers. */ \
+ 0x15) /* Mandatory set to one. */
+
+#define MAX_LOCALITIES 1 /* Eventually there could be five. */
+/* Volatile registers for FIFO mode */
+static struct tpm_register_file {
+ uint8_t tpm_access;
+ uint32_t tpm_int_status;
+ uint32_t tpm_sts;
+ uint8_t tpm_data_fifo[64]; /* this might have to be much deeper. */
+ uint32_t fifo_read_index; /* for read commands */
+ uint32_t fifo_write_index; /* for write commands */
+} tpm_regs;
+
+enum tpm_access_bits {
+ tpm_reg_valid_sts = (1 << 7),
+ active_locality = (1 << 5),
+ request_use = (1 << 1),
+ tpm_establishment = (1 << 0),
+};
+
+enum tpm_sts_bits {
+ tpm_family_shift = 26,
+ tpm_family_mask = ((1 << 2) - 1), /* 2 bits wide */
+ tpm_family_tpm2 = 1,
+ reset_establishment_bit = (1 << 25),
+ command_cancel = (1 << 24),
+ burst_count_shift = 8,
+ burst_count_mask = ((1 << 16) - 1), /* 16 bits wide */
+ sts_valid = (1 << 7),
+ command_ready = (1 << 6),
+ tpm_go = (1 << 5),
+ data_avail = (1 << 4),
+ expect = (1 << 3),
+ self_test_done = (1 << 2),
+ response_retry = (1 << 1),
+};
+
+/*
+ * NOTE: The put/get functions are called in interrupt context! Don't waste a
+ * lot of time here - just copy the data and wake up a task to deal with it
+ * later. Although if the implementation mandates a "busy" bit somewhere, you
+ * might want to set it now to avoid race conditions with back-to-back
+ * interrupts.
+ */
+
+static void copy_bytes(uint8_t *dest, uint32_t data_size, uint32_t value)
+{
+ unsigned real_size, i;
+
+ real_size = MIN(data_size, 4);
+
+ for (i = 0; i < real_size; i++)
+ dest[i] = (value >> (i * 8)) & 0xff;
+}
+
+static void access_reg_write(uint8_t data, uint8_t *reg)
+{
+ /* By definition only one bit can be set at a time. */
+ if (!data || (data & (data-1))) {
+ CPRINTF("%s: attempt to set acces reg to %02x\n",
+ __func__, data);
+ return;
+ }
+
+ switch (data) {
+ case request_use:
+ *reg |= active_locality;
+ break;
+
+ case active_locality:
+ *reg &= ~active_locality;
+ break;
+ }
+}
+
+void tpm_register_put(uint32_t regaddr, const uint8_t *data, uint32_t data_size)
+{
+ uint32_t i;
+ switch (regaddr) {
+ case TPM_ACCESS:
+ /* This is a one byte register, ignore extra data, if any */
+ access_reg_write(data[0], &tpm_regs.tpm_access);
+ break;
+ default:
+ CPRINTF("%s(0x%06x, %d", __func__, regaddr, data_size);
+ for (i = 0; i < data_size; i++)
+ CPRINTF(", %02x", data[i]);
+ CPRINTF("\n");
+ return;
+ }
+
+}
+
+void tpm_register_get(uint32_t regaddr, uint8_t *dest, uint32_t data_size)
+{
+ switch (regaddr) {
+ case TPM_DID_VID:
+ copy_bytes(dest, data_size, (GOOGLE_DID << 16) | GOOGLE_VID);
+ break;
+ case TPM_RID:
+ copy_bytes(dest, data_size, CR50_RID);
+ break;
+ case TPM_INTF_CAPABILITY:
+ copy_bytes(dest, data_size, IF_CAPABILITY_REG);
+ break;
+ case TPM_ACCESS:
+ copy_bytes(dest, data_size, tpm_regs.tpm_access);
+ break;
+ case TPM_STS:
+ copy_bytes(dest, data_size, tpm_regs.tpm_sts);
+ break;
+ default:
+ CPRINTS("%s(0x%06x, %d) => ??", __func__, regaddr, data_size);
+ return;
+ }
+}
+
+
+static void tpm_init(void)
+{
+ tpm_regs.tpm_access = tpm_reg_valid_sts;
+ tpm_regs.tpm_sts = (tpm_family_tpm2 << tpm_family_shift) |
+ (64 << burst_count_shift);
+}
+
+DECLARE_HOOK(HOOK_INIT, tpm_init, HOOK_PRIO_DEFAULT);
diff --git a/include/config.h b/include/config.h
index 17d0712b79..ad28fc8e71 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1457,6 +1457,12 @@
#undef CONFIG_TEMP_SENSOR_POWER_GPIO
/*****************************************************************************/
+/* TPM-like configuration */
+
+/* Speak the TPM SPI Hardware Protocol on the SPI slave interface */
+#undef CONFIG_TPM_SPS
+
+/*****************************************************************************/
/* USART stream config */
#undef CONFIG_STREAM_USART
diff --git a/include/tpm_registers.h b/include/tpm_registers.h
new file mode 100644
index 0000000000..cdf8410a51
--- /dev/null
+++ b/include/tpm_registers.h
@@ -0,0 +1,24 @@
+/* Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This defines the interface functions for TPM SPI Hardware Protocol. The SPI
+ * master reads or writes between 1 and 64 bytes to a register designated by a
+ * 24-bit address. There is no provision for error reporting at this level.
+ */
+
+#ifndef __CROS_EC_TPM_REGISTERS_H
+#define __CROS_EC_TPM_REGISTERS_H
+
+#include <stdint.h>
+
+/* The SPI master is writing data into a TPM register. */
+void tpm_register_put(uint32_t regaddr,
+ const uint8_t *data, uint32_t data_size);
+
+/* The SPI master is reading data from a TPM register. */
+void tpm_register_get(uint32_t regaddr, uint8_t *dest, uint32_t data_size);
+
+#endif /* __CROS_EC_TPM_REGISTERS_H */