summaryrefslogtreecommitdiff
path: root/src/drivers/pc80/tpm
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@chromium.org>2014-10-30 15:20:19 -0700
committerStefan Reinauer <stefan.reinauer@coreboot.org>2015-04-10 19:32:46 +0200
commitdd281edcfab464c4bf9df3fa9d86db5639c845e0 (patch)
treee7881cbc206a662386c20a7fbed9040f9e1b3731 /src/drivers/pc80/tpm
parent1ab1eac63ec4c13655e917eb34c86bbf290802a2 (diff)
tpm: Add ramstage driver and interrupt configuration
This adds a ramstage driver for the TPM and allows the interrupt to be configured in devicetree.cb. The interrupt vector is set like other PNP devices, and the interrupt polarity is set with a register configuration variable. These values are written into locality 0 TPM_INT_VECTOR and TPM_INT_ENABLE and then all interrupts are disabled so they are not used in firmware but can be enabled by the OS. It also adds an ACPI device for the TPM which will configure the reported interrupt based on what has been written into the TPM during ramstage. The _STA method returns enabled if CONFIG_LPC_TPM is enabled, and the _CRS method will only report an interrupt if one has been set in the TPM itself. The TPM memory address is added by the driver and declared in the ACPI code. In order to access it in ACPI a Kconfig entry is added for the default TPM TIS 1.2 base address. Note that IO address 0x2e is required to be declared in ACPI for the kernel driver to probe correctly. BUG=chrome-os-partner:33385 BRANCH=samus,auron TEST=manual testing on samus: 1) Add TPM device in devicetree.cb with configured interrupt and ensure that it is functional in the OS. 2) Test with active high and active low, edge triggered and level triggered setups. 3) Ensure that with no device added to devicetree.cb that the TPM is still functional in polling mode. Change-Id: Iee2a1832394dfe32f3ea3700753b8ecc443c7fbf Signed-off-by: Stefan Reinauer <reinauer@chromium.org> Original-Commit-Id: fc2c106caae939467fb07f3a0207adee71dda48e Original-Change-Id: Id8a5a251f193c71ab2209f85fb470120a3b6a80d Original-Signed-off-by: Duncan Laurie <dlaurie@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/226661 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: http://review.coreboot.org/9469 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Diffstat (limited to 'src/drivers/pc80/tpm')
-rw-r--r--src/drivers/pc80/tpm/Kconfig9
-rw-r--r--src/drivers/pc80/tpm/Makefile.inc1
-rw-r--r--src/drivers/pc80/tpm/acpi/tpm.asl104
-rw-r--r--src/drivers/pc80/tpm/chip.h35
-rw-r--r--src/drivers/pc80/tpm/tpm.c108
5 files changed, 257 insertions, 0 deletions
diff --git a/src/drivers/pc80/tpm/Kconfig b/src/drivers/pc80/tpm/Kconfig
index fbb3b423f4..9630de2ac1 100644
--- a/src/drivers/pc80/tpm/Kconfig
+++ b/src/drivers/pc80/tpm/Kconfig
@@ -5,3 +5,12 @@ config LPC_TPM
Enable this option to enable LPC TPM support in coreboot.
If unsure, say N.
+
+config TPM_TIS_BASE_ADDRESS
+ hex "TPM Base Address"
+ default 0xfed40000
+ help
+ This can be used to adjust the TPM memory base address.
+ The default is specified by the TCG PC Client Specific TPM
+ Interface Specification 1.2 and should not be changed unless
+ the TPM being used does not conform to TPM TIS 1.2.
diff --git a/src/drivers/pc80/tpm/Makefile.inc b/src/drivers/pc80/tpm/Makefile.inc
index 6f973e12f1..c900fe8c1d 100644
--- a/src/drivers/pc80/tpm/Makefile.inc
+++ b/src/drivers/pc80/tpm/Makefile.inc
@@ -1 +1,2 @@
romstage-$(CONFIG_LPC_TPM) += tpm.c
+ramstage-$(CONFIG_LPC_TPM) += tpm.c
diff --git a/src/drivers/pc80/tpm/acpi/tpm.asl b/src/drivers/pc80/tpm/acpi/tpm.asl
new file mode 100644
index 0000000000..7500211570
--- /dev/null
+++ b/src/drivers/pc80/tpm/acpi/tpm.asl
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 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
+ */
+
+/* Trusted Platform Module */
+
+Device (TPM)
+{
+ Name (_HID, EISAID ("PNP0C31"))
+ Name (_CID, 0x310cd041)
+ Name (_UID, 1)
+
+ Method (_STA, 0)
+ {
+ If (CONFIG_LPC_TPM) {
+ Return (0xf)
+ } Else {
+ Return (0x0)
+ }
+ }
+
+ Name (IBUF, ResourceTemplate ()
+ {
+ /* Updated based on TPM interrupt for Locality 0 */
+ Interrupt (ResourceConsumer, Edge, ActiveHigh,
+ Exclusive, , , TIRQ) { 0 }
+ })
+
+ Name (RBUF, ResourceTemplate ()
+ {
+ IO (Decode16, 0x2e, 0x2e, 0x01, 0x02)
+ Memory32Fixed (ReadWrite, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000)
+ })
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ OperationRegion (TREG, SystemMemory,
+ CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000)
+ Field (TREG, ByteAcc, NoLock, Preserve)
+ {
+ /* TPM_INT_ENABLE_0 */
+ Offset (0x0008),
+ , 3,
+ ITPL, 2, /* Interrupt type and polarity */
+
+ /* TPM_INT_VECTOR_0 */
+ Offset (0x000C),
+ IVEC, 4, /* SERIRQ vector */
+ }
+
+ If (LGreater (IVEC, 0)) {
+ /* Update interrupt vector */
+ CreateField (^IBUF, ^TIRQ._INT, 32, TVEC)
+ Store (IVEC, TVEC)
+
+ /* Update interrupt type and polarity */
+ CreateBitField (^IBUF, ^TIRQ._HE, TTYP)
+ CreateBitField (^IBUF, ^TIRQ._LL, TPOL)
+ CreateBitField (^IBUF, ^TIRQ._SHR, TSHR)
+
+ If (LEqual (ITPL, 0x0)) {
+ /* Active-High Level-Triggered Shared */
+ Store (Zero, TPOL)
+ Store (Zero, TTYP)
+ Store (One, TSHR)
+ } ElseIf (LEqual (ITPL, 0x1)) {
+ /* Active-Low Level-Triggered Shared */
+ Store (One, TPOL)
+ Store (Zero, TTYP)
+ Store (One, TSHR)
+ } ElseIf (LEqual (ITPL, 0x2)) {
+ /* Active-High Edge-Triggered Exclusive */
+ Store (Zero, TPOL)
+ Store (One, TTYP)
+ Store (Zero, TSHR)
+ } ElseIf (LEqual (ITPL, 0x3)) {
+ /* Active-Low Edge-Triggered Exclusive */
+ Store (One, TPOL)
+ Store (One, TTYP)
+ Store (Zero, TSHR)
+ }
+
+ /* Merge IRQ with base address */
+ Return (ConcatenateResTemplate (RBUF, IBUF))
+ } Else {
+ Return (RBUF)
+ }
+ }
+}
diff --git a/src/drivers/pc80/tpm/chip.h b/src/drivers/pc80/tpm/chip.h
new file mode 100644
index 0000000000..bcfb877dfa
--- /dev/null
+++ b/src/drivers/pc80/tpm/chip.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * 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 DRIVERS_PC80_TPM_CHIP_H
+#define DRIVERS_PC80_TPM_CHIP_H
+
+typedef struct drivers_pc80_tpm_config {
+ /*
+ * TPM Interrupt polarity:
+ *
+ * High Level 0
+ * Low Level 1
+ * Rising Edge 2
+ * Falling Edge 3
+ */
+ u8 irq_polarity;
+} tpm_config_t;
+
+#endif /* DRIVERS_PC80_TPM_CHIP_H */
diff --git a/src/drivers/pc80/tpm/tpm.c b/src/drivers/pc80/tpm/tpm.c
index c0429ddc9c..f1c5d97810 100644
--- a/src/drivers/pc80/tpm/tpm.c
+++ b/src/drivers/pc80/tpm/tpm.c
@@ -35,6 +35,8 @@
#include <console/console.h>
#include <tpm.h>
#include <arch/early_variables.h>
+#include <device/pnp.h>
+#include "chip.h"
#define PREFIX "lpc_tpm: "
@@ -211,6 +213,20 @@ static inline u32 tpm_read_did_vid(int locality)
return value;
}
+static inline void tpm_write_int_vector(int vector, int locality)
+{
+ TPM_DEBUG_IO_WRITE(TIS_REG_INT_VECTOR, vector);
+ writeb(vector & 0xf, TIS_REG(locality, TIS_REG_INT_VECTOR));
+}
+
+static inline void tpm_write_int_polarity(int polarity, int locality)
+{
+ /* Set polarity and leave all other bits at 0 */
+ u32 value = (polarity & 0x3) << 3;
+ TPM_DEBUG_IO_WRITE(TIS_REG_INT_ENABLE, value);
+ writel(value, TIS_REG(locality, TIS_REG_INT_ENABLE));
+}
+
/*
* tis_wait_sts()
*
@@ -673,3 +689,95 @@ int tis_sendrecv(const uint8_t *sendbuf, size_t send_size,
return tis_readresponse(recvbuf, recv_len);
}
+
+#ifdef __RAMSTAGE__
+
+/*
+ * tis_setup_interrupt()
+ *
+ * Set up the interrupt vector and polarity for locality 0 and
+ * disable all interrupts so they are unused in firmware but can
+ * be enabled by the OS.
+ *
+ * The values used here must match what is passed in the TPM ACPI
+ * device if ACPI is used on the platform.
+ *
+ * @vector - TPM interrupt vector
+ * @polarity - TPM interrupt polarity
+ *
+ * Returns 0 on success, TPM_DRIVER_ERR on failure.
+ */
+static int tis_setup_interrupt(int vector, int polarity)
+{
+ u8 locality = 0;
+ int has_access = tis_has_access(locality);
+
+ /* Open connection and request access if not already granted */
+ if (!has_access && tis_open() < 0)
+ return TPM_DRIVER_ERR;
+
+ /* Set TPM interrupt vector */
+ tpm_write_int_vector(vector, locality);
+
+ /* Set TPM interupt polarity and disable interrupts */
+ tpm_write_int_polarity(polarity, locality);
+
+ /* Close connection if it was opened */
+ if (!has_access && tis_close() < 0)
+ return TPM_DRIVER_ERR;
+
+ return 0;
+}
+
+static void lpc_tpm_read_resources(struct device *dev)
+{
+ /* Static 5K memory region specified in Kconfig */
+ mmio_resource(dev, 0, CONFIG_TPM_TIS_BASE_ADDRESS >> 10, 0x5000 >> 10);
+}
+
+static void lpc_tpm_set_resources(struct device *dev)
+{
+ tpm_config_t *config = (tpm_config_t *)dev->chip_info;
+ struct resource *res;
+
+ for (res = dev->resource_list; res; res = res->next) {
+ if (!(res->flags & IORESOURCE_ASSIGNED))
+ continue;
+
+ if (res->flags & IORESOURCE_IRQ) {
+ /* Set interrupt vector */
+ tis_setup_interrupt((int)res->base,
+ config->irq_polarity);
+ } else {
+ printk(BIOS_ERR,
+ "ERROR: %s %02lx unknown resource type\n",
+ dev_path(dev), res->index);
+ continue;
+ }
+
+ res->flags |= IORESOURCE_STORED;
+ report_resource_stored(dev, res, " <tpm>");
+ }
+}
+
+static struct device_operations lpc_tpm_ops = {
+ .read_resources = &lpc_tpm_read_resources,
+ .set_resources = &lpc_tpm_set_resources,
+};
+
+static struct pnp_info pnp_dev_info[] = {
+ { .flags = PNP_IRQ0 }
+};
+
+static void enable_dev(struct device *dev)
+{
+ pnp_enable_devices(dev, &lpc_tpm_ops,
+ ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
+}
+
+struct chip_operations drivers_pc80_tpm_ops = {
+ CHIP_NAME("LPC TPM")
+ .enable_dev = enable_dev
+};
+
+#endif /* __RAMSTAGE__ */