Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9504
-gerrit
commit a97b992be1d172c57e244464a843d5f2dc317247
Author: Lee Leahy <leroy.p.leahy(a)intel.com>
Date: Mon Feb 9 21:09:49 2015 -0800
x86: Support reset routines in bootblock
Expand the boot block include file to allow for a file containing reset
routines to be added. Prevent breaking existing platforms by using a
Kconfig value to specify the path to this file, and have the code
include this file only if the Kconfig value is set.
BRANCH=none
BUG=None
TEST=Build and run on Glados
Change-Id: I604f701057d7018f2ed9c3ba49a643c4bca13f00
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: c109481d9503916e19ed300c1a3f085e0d2b5c51
Original-Change-Id: I3214399f8156b5ea2ef709ce77e3915cea1523a3
Original-Signed-off-by: Lee Leahy <Leroy.P.Leahy(a)intel.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/248300
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
Original-Commit-Queue: Leroy P Leahy <leroy.p.leahy(a)intel.com>
Original-Tested-by: Leroy P Leahy <leroy.p.leahy(a)intel.com>
---
src/arch/x86/Kconfig | 3 +++
src/arch/x86/include/bootblock_common.h | 4 ++++
2 files changed, 7 insertions(+)
diff --git a/src/arch/x86/Kconfig b/src/arch/x86/Kconfig
index f7da89a..29f0514 100644
--- a/src/arch/x86/Kconfig
+++ b/src/arch/x86/Kconfig
@@ -84,6 +84,9 @@ config BOOTBLOCK_MAINBOARD_INIT
config BOOTBLOCK_NORTHBRIDGE_INIT
string
+config BOOTBLOCK_RESETS
+ string
+
config HAVE_CMOS_DEFAULT
def_bool n
diff --git a/src/arch/x86/include/bootblock_common.h b/src/arch/x86/include/bootblock_common.h
index b4100b7..939ba08 100644
--- a/src/arch/x86/include/bootblock_common.h
+++ b/src/arch/x86/include/bootblock_common.h
@@ -2,6 +2,10 @@
#include <cpu/x86/lapic/boot_cpu.c>
#include <pc80/mc146818rtc.h>
+#ifdef CONFIG_BOOTBLOCK_RESETS
+#include CONFIG_BOOTBLOCK_RESETS
+#endif
+
#ifdef CONFIG_BOOTBLOCK_CPU_INIT
#include CONFIG_BOOTBLOCK_CPU_INIT
#endif
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9469
-gerrit
commit 3f47c508c934d39156ddcf1190d3fd350b130ebd
Author: Duncan Laurie <dlaurie(a)chromium.org>
Date: Thu Oct 30 15:20:19 2014 -0700
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(a)chromium.org>
Original-Commit-Id: fc2c106caae939467fb07f3a0207adee71dda48e
Original-Change-Id: Id8a5a251f193c71ab2209f85fb470120a3b6a80d
Original-Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/226661
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/drivers/pc80/tpm/Kconfig | 9 ++++
src/drivers/pc80/tpm/Makefile.inc | 1 +
src/drivers/pc80/tpm/acpi/tpm.asl | 104 ++++++++++++++++++++++++++++++++++++
src/drivers/pc80/tpm/chip.h | 35 ++++++++++++
src/drivers/pc80/tpm/tpm.c | 108 ++++++++++++++++++++++++++++++++++++++
5 files changed, 257 insertions(+)
diff --git a/src/drivers/pc80/tpm/Kconfig b/src/drivers/pc80/tpm/Kconfig
index fbb3b42..9630de2 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 6f973e1..c900fe8 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 0000000..7500211
--- /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 0000000..bcfb877
--- /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 c0429dd..f1c5d97 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__ */
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9507
-gerrit
commit 55efc85701c0e83997a531a5dc99a9d8ceb58cc2
Author: Julius Werner <jwerner(a)chromium.org>
Date: Fri Feb 20 13:36:11 2015 -0800
cros_ec: Retry failed VBNV transactions
This patch adds a few retries to NVRAM read/write transactions with the
EC. Failing to read the NVRAM is not fatal to the boot, but it's still
pretty bad... especially since a single initial read failure will cause
vboot to blindly reinitialize the whole NVRAM with zeroes, destroying
important configuration bits like dev_boot_usb. The current EC
transaction timeout is one second, so the three retries added here can
potentially increase boot time by three seconds per transaction... but
this shouldn't happen in any normal case anyway, and if there are errors
a little extra wait is probably preferrable to nuking your NVRAM.
(Also, added a missing newline to an error message in the EC code.)
BRANCH=veyron
BUG=chrome-os-partner:36924
TEST=Booted a Jerry with the power button bug with a 2 second press,
noticed that the first two transactions failed but the third one
succeeded.
Change-Id: I5d1cf29ac1c555ea2336ebb0b0e0a3f7cbb9c3fd
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: 894a8a0b4a9805e92544b5e3dfa90baf6d36649a
Original-Change-Id: I6267cdda2be2bad34541b687404c2434d3be345b
Original-Signed-off-by: Julius Werner <jwerner(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/251694
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/ec/google/chromeec/crosec_proto.c | 2 +-
src/ec/google/chromeec/ec.c | 11 +++++++++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/ec/google/chromeec/crosec_proto.c b/src/ec/google/chromeec/crosec_proto.c
index 55a707d..1183e82 100644
--- a/src/ec/google/chromeec/crosec_proto.c
+++ b/src/ec/google/chromeec/crosec_proto.c
@@ -235,7 +235,7 @@ static int send_command_proto3(struct chromeec_command *cec_command,
rv = crosec_io(out_bytes, in_bytes, context);
if (rv != 0) {
- printk(BIOS_ERR, "%s: failed to complete I/O: Err = %#x.",
+ printk(BIOS_ERR, "%s: failed to complete I/O: Err = %#x.\n",
__func__, rv >= 0 ? rv : -rv);
return -EC_RES_ERROR;
}
diff --git a/src/ec/google/chromeec/ec.c b/src/ec/google/chromeec/ec.c
index 83c22d3..bda88e4 100644
--- a/src/ec/google/chromeec/ec.c
+++ b/src/ec/google/chromeec/ec.c
@@ -232,11 +232,12 @@ int google_chromeec_vbnv_context(int is_read, uint8_t *data, int len)
struct chromeec_command cec_cmd;
struct ec_params_vbnvcontext cmd_vbnvcontext;
struct ec_response_vbnvcontext rsp_vbnvcontext;
+ int retries = 3;
if (len != EC_VBNV_BLOCK_SIZE)
return -1;
-
+ retry:
cec_cmd.cmd_code = EC_CMD_VBNV_CONTEXT;
cec_cmd.cmd_version = EC_VER_VBNV_CONTEXT;
cec_cmd.cmd_data_in = &cmd_vbnvcontext;
@@ -251,7 +252,13 @@ int google_chromeec_vbnv_context(int is_read, uint8_t *data, int len)
if (!is_read)
memcpy(&cmd_vbnvcontext.block, data, EC_VBNV_BLOCK_SIZE);
- google_chromeec_command(&cec_cmd);
+ if (google_chromeec_command(&cec_cmd)) {
+ printk(BIOS_ERR, "ERROR: failed to %s vbnv_ec context: %d\n",
+ is_read ? "read" : "write", (int)cec_cmd.cmd_code);
+ mdelay(10); /* just in case */
+ if (--retries)
+ goto retry;
+ }
if (is_read)
memcpy(data, &rsp_vbnvcontext.block, EC_VBNV_BLOCK_SIZE);
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9468
-gerrit
commit 8e560dd2251f25732cfb16d6fd1356497c638591
Author: Duncan Laurie <dlaurie(a)chromium.org>
Date: Thu Oct 30 15:11:48 2014 -0700
tpm: Move the LPC TPM driver to a subdirectory
This moves the LPC TPM driver to drivers/pc80/tpm so it can
be turned into a ramstage driver with a chip.h
It includes no other changes yet.
BUG=chrome-os-partner:33385
BRANCH=samus,auron
TEST=emerge-samus coreboot
Change-Id: Iac83e52db96201f37a0086eae9df244f8b8d48d9
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: be2db391f9da80b8b75137af0fe81dc4724bc9d1
Original-Change-Id: I60ddd1d2a3e72bcf169a0b44e0c7ebcb87f4617d
Original-Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/226660
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/drivers/pc80/Kconfig | 10 +-
src/drivers/pc80/Makefile.inc | 3 +-
src/drivers/pc80/tpm.c | 675 --------------------------------------
src/drivers/pc80/tpm/Kconfig | 7 +
src/drivers/pc80/tpm/Makefile.inc | 1 +
src/drivers/pc80/tpm/tpm.c | 675 ++++++++++++++++++++++++++++++++++++++
6 files changed, 686 insertions(+), 685 deletions(-)
diff --git a/src/drivers/pc80/Kconfig b/src/drivers/pc80/Kconfig
index f6da88c..a1e94ab 100644
--- a/src/drivers/pc80/Kconfig
+++ b/src/drivers/pc80/Kconfig
@@ -17,16 +17,10 @@ config DRIVERS_PS2_KEYBOARD
this option, then you can say N here to speed up boot time.
Otherwise say Y.
-config LPC_TPM
- bool
- default n
- help
- Enable this option to enable TPM support in coreboot.
-
- If unsure, say N.
-
config DRIVERS_MC146818
bool
default y if ARCH_X86
+source src/drivers/pc80/tpm/Kconfig
+
endif
diff --git a/src/drivers/pc80/Makefile.inc b/src/drivers/pc80/Makefile.inc
index 9715949..be8e8ea 100644
--- a/src/drivers/pc80/Makefile.inc
+++ b/src/drivers/pc80/Makefile.inc
@@ -9,10 +9,9 @@ ramstage-$(CONFIG_SPKMODEM) += spkmodem.c
romstage-$(CONFIG_DRIVERS_MC146818) += mc146818rtc_early.c
-romstage-$(CONFIG_LPC_TPM) += tpm.c
romstage-$(CONFIG_SPKMODEM) += spkmodem.c
-subdirs-y += vga
+subdirs-y += tpm vga
cbfs-files-$(CONFIG_HAVE_CMOS_DEFAULT) += cmos.default
cmos.default-file = $(CONFIG_CMOS_DEFAULT_FILE):nvramtool
diff --git a/src/drivers/pc80/tpm.c b/src/drivers/pc80/tpm.c
deleted file mode 100644
index c0429dd..0000000
--- a/src/drivers/pc80/tpm.c
+++ /dev/null
@@ -1,675 +0,0 @@
-/*
- * This file is part of the coreboot project.
- *
- * Copyright (C) 2011 The Chromium OS Authors. 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
- */
-
-/*
- * The code in this file has been heavily based on the article "Writing a TPM
- * Device Driver" published on http://ptgmedia.pearsoncmg.com and the
- * submission by Stefan Berger on Qemu-devel mailing list.
- *
- * One principal difference is that in the simplest config the other than 0
- * TPM localities do not get mapped by some devices (for instance, by
- * Infineon slb9635), so this driver provides access to locality 0 only.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <delay.h>
-#include <arch/io.h>
-#include <arch/byteorder.h>
-#include <console/console.h>
-#include <tpm.h>
-#include <arch/early_variables.h>
-
-#define PREFIX "lpc_tpm: "
-
-/* coreboot wrapper for TPM driver (start) */
-#define TPM_DEBUG(fmt, args...) \
- if (CONFIG_DEBUG_TPM) { \
- printk(BIOS_DEBUG, PREFIX); \
- printk(BIOS_DEBUG, fmt , ##args); \
- }
-#define TPM_DEBUG_IO_READ(reg_, val_) \
- TPM_DEBUG("Read reg 0x%x returns 0x%x\n", (reg_), (val_))
-#define TPM_DEBUG_IO_WRITE(reg_, val_) \
- TPM_DEBUG("Write reg 0x%x with 0x%x\n", (reg_), (val_))
-#define printf(x...) printk(BIOS_ERR, x)
-
-#define readb(_a) (*(volatile unsigned char *) (_a))
-#define writeb(_v, _a) (*(volatile unsigned char *) (_a) = (_v))
-#define readl(_a) (*(volatile unsigned long *) (_a))
-#define writel(_v, _a) (*(volatile unsigned long *) (_a) = (_v))
-/* coreboot wrapper for TPM driver (end) */
-
-#ifndef CONFIG_TPM_TIS_BASE_ADDRESS
-/* Base TPM address standard for x86 systems */
-#define CONFIG_TPM_TIS_BASE_ADDRESS 0xfed40000
-#endif
-
-/* the macro accepts the locality value, but only locality 0 is operational */
-#define TIS_REG(LOCALITY, REG) \
- (void *)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG)
-
-/* hardware registers' offsets */
-#define TIS_REG_ACCESS 0x0
-#define TIS_REG_INT_ENABLE 0x8
-#define TIS_REG_INT_VECTOR 0xc
-#define TIS_REG_INT_STATUS 0x10
-#define TIS_REG_INTF_CAPABILITY 0x14
-#define TIS_REG_STS 0x18
-#define TIS_REG_BURST_COUNT 0x19
-#define TIS_REG_DATA_FIFO 0x24
-#define TIS_REG_DID_VID 0xf00
-#define TIS_REG_RID 0xf04
-
-/* Some registers' bit field definitions */
-#define TIS_STS_VALID (1 << 7) /* 0x80 */
-#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
-#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
-#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
-#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
-#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
-
-#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
-#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
-#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
-#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
-#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
-#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
-#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
-
-/*
- * Error value returned if a tpm register does not enter the expected state
- * after continuous polling. No actual TPM register reading ever returns ~0,
- * so this value is a safe error indication to be mixed with possible status
- * register values.
- */
-#define TPM_TIMEOUT_ERR (~0)
-
-/* Error value returned on various TPM driver errors */
-#define TPM_DRIVER_ERR (~0)
-
- /* 1 second is plenty for anything TPM does.*/
-#define MAX_DELAY_US (1000 * 1000)
-
-/*
- * Structures defined below allow creating descriptions of TPM vendor/device
- * ID information for run time discovery. The only device the system knows
- * about at this time is Infineon slb9635
- */
-struct device_name {
- u16 dev_id;
- const char * const dev_name;
-};
-
-struct vendor_name {
- u16 vendor_id;
- const char * vendor_name;
- const struct device_name* dev_names;
-};
-
-static const struct device_name atmel_devices[] = {
- {0x3204, "AT97SC3204"},
- {0xffff}
-};
-
-static const struct device_name infineon_devices[] = {
- {0x000b, "SLB9635 TT 1.2"},
- {0xffff}
-};
-
-static const struct device_name nuvoton_devices[] = {
- {0x00fe, "NPCT420AA V2"},
- {0xffff}
-};
-
-static const struct device_name stmicro_devices[] = {
- {0x0000, "ST33ZP24" },
- {0xffff}
-};
-
-static const struct vendor_name vendor_names[] = {
- {0x1114, "Atmel", atmel_devices},
- {0x15d1, "Infineon", infineon_devices},
- {0x1050, "Nuvoton", nuvoton_devices},
- {0x104a, "ST Microelectronics", stmicro_devices},
-};
-
-/*
- * Cached vendor/device ID pair to indicate that the device has been already
- * discovered
- */
-static u32 vendor_dev_id CAR_GLOBAL;
-
-static inline u8 tpm_read_status(int locality)
-{
- u8 value = readb(TIS_REG(locality, TIS_REG_STS));
- TPM_DEBUG_IO_READ(TIS_REG_STS, value);
- return value;
-}
-
-static inline void tpm_write_status(u8 sts, int locality)
-{
- TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts);
- writeb(sts, TIS_REG(locality, TIS_REG_STS));
-}
-
-static inline u8 tpm_read_data(int locality)
-{
- u8 value = readb(TIS_REG(locality, TIS_REG_DATA_FIFO));
- TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value);
- return value;
-}
-
-static inline void tpm_write_data(u8 data, int locality)
-{
- TPM_DEBUG_IO_WRITE(TIS_REG_STS, data);
- writeb(data, TIS_REG(locality, TIS_REG_DATA_FIFO));
-}
-
-static inline u16 tpm_read_burst_count(int locality)
-{
- u16 count;
- count = readb(TIS_REG(locality, TIS_REG_BURST_COUNT));
- count |= readb(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8;
- TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count);
- return count;
-}
-
-static inline u8 tpm_read_access(int locality)
-{
- u8 value = readb(TIS_REG(locality, TIS_REG_ACCESS));
- TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value);
- return value;
-}
-
-static inline void tpm_write_access(u8 data, int locality)
-{
- TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data);
- writeb(data, TIS_REG(locality, TIS_REG_ACCESS));
-}
-
-static inline u32 tpm_read_did_vid(int locality)
-{
- u32 value = readl(TIS_REG(locality, TIS_REG_DID_VID));
- TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value);
- return value;
-}
-
-/*
- * tis_wait_sts()
- *
- * Wait for at least a second for a status to change its state to match the
- * expected state. Normally the transition happens within microseconds.
- *
- * @locality - locality
- * @mask - bitmask for the bitfield(s) to watch
- * @expected - value the field(s) are supposed to be set to
- *
- * Returns 0 on success or TPM_TIMEOUT_ERR on timeout.
- */
-static int tis_wait_sts(int locality, u8 mask, u8 expected)
-{
- u32 time_us = MAX_DELAY_US;
- while (time_us > 0) {
- u8 value = tpm_read_status(locality);
- if ((value & mask) == expected)
- return 0;
- udelay(1); /* 1 us */
- time_us--;
- }
- return TPM_TIMEOUT_ERR;
-}
-
-static inline int tis_wait_ready(int locality)
-{
- return tis_wait_sts(locality, TIS_STS_COMMAND_READY,
- TIS_STS_COMMAND_READY);
-}
-
-static inline int tis_wait_valid(int locality)
-{
- return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID);
-}
-
-static inline int tis_wait_valid_data(int locality)
-{
- const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
- return tis_wait_sts(locality, has_data, has_data);
-}
-
-static inline int tis_has_valid_data(int locality)
-{
- const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
- return (tpm_read_status(locality) & has_data) == has_data;
-}
-
-static inline int tis_expect_data(int locality)
-{
- return !!(tpm_read_status(locality) & TIS_STS_EXPECT);
-}
-
-/*
- * tis_wait_access()
- *
- * Wait for at least a second for a access to change its state to match the
- * expected state. Normally the transition happens within microseconds.
- *
- * @locality - locality
- * @mask - bitmask for the bitfield(s) to watch
- * @expected - value the field(s) are supposed to be set to
- *
- * Returns 0 on success or TPM_TIMEOUT_ERR on timeout.
- */
-static int tis_wait_access(int locality, u8 mask, u8 expected)
-{
- u32 time_us = MAX_DELAY_US;
- while (time_us > 0) {
- u8 value = tpm_read_access(locality);
- if ((value & mask) == expected)
- return 0;
- udelay(1); /* 1 us */
- time_us--;
- }
- return TPM_TIMEOUT_ERR;
-}
-
-static inline int tis_wait_dropped_access(int locality)
-{
- return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY, 0);
-}
-
-static inline int tis_wait_received_access(int locality)
-{
- return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY,
- TIS_ACCESS_ACTIVE_LOCALITY);
-}
-
-static inline int tis_has_access(int locality)
-{
- return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY);
-}
-
-static inline void tis_request_access(int locality)
-{
- tpm_write_access(TIS_ACCESS_REQUEST_USE, locality);
-}
-
-static inline void tis_drop_access(int locality)
-{
- tpm_write_access(TIS_ACCESS_ACTIVE_LOCALITY, locality);
-}
-
-/*
- * PC Client Specific TPM Interface Specification section 11.2.12:
- *
- * Software must be prepared to send two writes of a "1" to command ready
- * field: the first to indicate successful read of all the data, thus
- * clearing the data from the ReadFIFO and freeing the TPM's resources,
- * and the second to indicate to the TPM it is about to send a new command.
- *
- * In practice not all TPMs behave the same so it is necessary to be
- * flexible when trying to set command ready.
- *
- * Returns 0 on success if the TPM is ready for transactions.
- * Returns TPM_TIMEOUT_ERR if the command ready bit does not get set.
- */
-static int tis_command_ready(u8 locality)
-{
- u32 status;
-
- /* 1st attempt to set command ready */
- tpm_write_status(TIS_STS_COMMAND_READY, locality);
-
- /* Wait for response */
- status = tpm_read_status(locality);
-
- /* Check if command ready is set yet */
- if (status & TIS_STS_COMMAND_READY)
- return 0;
-
- /* 2nd attempt to set command ready */
- tpm_write_status(TIS_STS_COMMAND_READY, locality);
-
- return tis_wait_ready(locality);
-}
-
-/*
- * Probe the TPM device and try determining its manufacturer/device name.
- *
- * Returns 0 on success (the device is found or was found during an earlier
- * invocation) or TPM_DRIVER_ERR if the device is not found.
- */
-static u32 tis_probe(void)
-{
- const char *device_name = "unknown";
- const char *vendor_name = device_name;
- const struct device_name *dev;
- u32 didvid;
- u16 vid, did;
- int i;
-
- if (car_get_var(vendor_dev_id))
- return 0; /* Already probed. */
-
- didvid = tpm_read_did_vid(0);
- if (!didvid || (didvid == 0xffffffff)) {
- printf("%s: No TPM device found\n", __FUNCTION__);
- return TPM_DRIVER_ERR;
- }
-
- car_set_var(vendor_dev_id, didvid);
-
- vid = didvid & 0xffff;
- did = (didvid >> 16) & 0xffff;
- for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
- int j = 0;
- u16 known_did;
- if (vid == vendor_names[i].vendor_id) {
- vendor_name = vendor_names[i].vendor_name;
- } else {
- continue;
- }
- dev = &vendor_names[i].dev_names[j];
- while ((known_did = dev->dev_id) != 0xffff) {
- if (known_did == did) {
- device_name = dev->dev_name;
- break;
- }
- j++;
- }
- break;
- }
- /* this will have to be converted into debug printout */
- printf("Found TPM %s by %s\n", device_name, vendor_name);
- return 0;
-}
-
-/*
- * tis_senddata()
- *
- * send the passed in data to the TPM device.
- *
- * @data - address of the data to send, byte by byte
- * @len - length of the data to send
- *
- * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
- * not accept the entire command).
- */
-static u32 tis_senddata(const u8 * const data, u32 len)
-{
- u32 offset = 0;
- u16 burst = 0;
- u32 max_cycles = 0;
- u8 locality = 0;
-
- if (tis_wait_ready(locality)) {
- printf("%s:%d - failed to get 'command_ready' status\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
- burst = tpm_read_burst_count(locality);
-
- while (1) {
- unsigned count;
-
- /* Wait till the device is ready to accept more data. */
- while (!burst) {
- if (max_cycles++ == MAX_DELAY_US) {
- printf("%s:%d failed to feed %d bytes of %d\n",
- __FILE__, __LINE__, len - offset, len);
- return TPM_DRIVER_ERR;
- }
- udelay(1);
- burst = tpm_read_burst_count(locality);
- }
-
- max_cycles = 0;
-
- /*
- * Calculate number of bytes the TPM is ready to accept in one
- * shot.
- *
- * We want to send the last byte outside of the loop (hence
- * the -1 below) to make sure that the 'expected' status bit
- * changes to zero exactly after the last byte is fed into the
- * FIFO.
- */
- count = min(burst, len - offset - 1);
- while (count--)
- tpm_write_data(data[offset++], locality);
-
- if (tis_wait_valid(locality) || !tis_expect_data(locality)) {
- printf("%s:%d TPM command feed overflow\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- burst = tpm_read_burst_count(locality);
- if ((offset == (len - 1)) && burst)
- /*
- * We need to be able to send the last byte to the
- * device, so burst size must be nonzero before we
- * break out.
- */
- break;
- }
-
- /* Send the last byte. */
- tpm_write_data(data[offset++], locality);
-
- /*
- * Verify that TPM does not expect any more data as part of this
- * command.
- */
- if (tis_wait_valid(locality) || tis_expect_data(locality)) {
- printf("%s:%d unexpected TPM status 0x%x\n",
- __FILE__, __LINE__, tpm_read_status(locality));
- return TPM_DRIVER_ERR;
- }
-
- /* OK, sitting pretty, let's start the command execution. */
- tpm_write_status(TIS_STS_TPM_GO, locality);
-
- return 0;
-}
-
-/*
- * tis_readresponse()
- *
- * read the TPM device response after a command was issued.
- *
- * @buffer - address where to read the response, byte by byte.
- * @len - pointer to the size of buffer
- *
- * On success stores the number of received bytes to len and returns 0. On
- * errors (misformatted TPM data or synchronization problems) returns
- * TPM_DRIVER_ERR.
- */
-static u32 tis_readresponse(u8 *buffer, size_t *len)
-{
- u16 burst_count;
- u32 offset = 0;
- u8 locality = 0;
- u32 expected_count = *len;
- int max_cycles = 0;
-
- /* Wait for the TPM to process the command */
- if (tis_wait_valid_data(locality)) {
- printf("%s:%d failed processing command\n", __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- do {
- while ((burst_count = tpm_read_burst_count(locality)) == 0) {
- if (max_cycles++ == MAX_DELAY_US) {
- printf("%s:%d TPM stuck on read\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
- udelay(1);
- }
-
- max_cycles = 0;
-
- while (burst_count-- && (offset < expected_count)) {
- buffer[offset++] = tpm_read_data(locality);
- if (offset == 6) {
- /*
- * We got the first six bytes of the reply,
- * let's figure out how many bytes to expect
- * total - it is stored as a 4 byte number in
- * network order, starting with offset 2 into
- * the body of the reply.
- */
- u32 real_length;
- memcpy(&real_length,
- buffer + 2,
- sizeof(real_length));
- expected_count = be32_to_cpu(real_length);
-
- if ((expected_count < offset) ||
- (expected_count > *len)) {
- printf("%s:%d bad response size %d\n",
- __FILE__, __LINE__,
- expected_count);
- return TPM_DRIVER_ERR;
- }
- }
- }
-
- /* Wait for the next portion */
- if (tis_wait_valid(locality)) {
- printf("%s:%d failed to read response\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- if (offset == expected_count)
- break; /* We got all we need */
-
- } while (tis_has_valid_data(locality));
-
- /* * Make sure we indeed read all there was. */
- if (tis_has_valid_data(locality)) {
- printf("%s:%d wrong receive status: %x %d bytes left\n",
- __FILE__, __LINE__, tpm_read_status(locality),
- tpm_read_burst_count(locality));
- return TPM_DRIVER_ERR;
- }
-
- /* Tell the TPM that we are done. */
- if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
- return TPM_DRIVER_ERR;
-
- *len = offset;
- return 0;
-}
-
-/*
- * tis_init()
- *
- * Initialize the TPM device. Returns 0 on success or TPM_DRIVER_ERR on
- * failure (in case device probing did not succeed).
- */
-int tis_init(void)
-{
- if (tis_probe())
- return TPM_DRIVER_ERR;
- return 0;
-}
-
-/*
- * 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, TPM_DRIVER_ERR on failure.
- */
-int tis_open(void)
-{
- u8 locality = 0; /* we use locality zero for everything */
-
- if (tis_close())
- return TPM_DRIVER_ERR;
-
- /* now request access to locality */
- tis_request_access(locality);
-
- /* did we get a lock? */
- if (tis_wait_received_access(locality)) {
- printf("%s:%d - failed to lock locality %d\n",
- __FILE__, __LINE__, locality);
- return TPM_DRIVER_ERR;
- }
-
- /* Certain TPMs seem to need some delay here or they hang... */
- udelay(10);
-
- if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
- return TPM_DRIVER_ERR;
-
- return 0;
-}
-
-/*
- * tis_close()
- *
- * terminate the current session with the TPM by releasing the locked
- * locality. Returns 0 on success of TPM_DRIVER_ERR on failure (in case lock
- * removal did not succeed).
- */
-int tis_close(void)
-{
- u8 locality = 0;
- if (tis_has_access(locality)) {
- tis_drop_access(locality);
- if (tis_wait_dropped_access(locality)) {
- printf("%s:%d - failed to release locality %d\n",
- __FILE__, __LINE__, locality);
- return TPM_DRIVER_ERR;
- }
- }
- return 0;
-}
-
-/*
- * 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 TPM_DRIVER_ERR on failure.
- */
-int tis_sendrecv(const uint8_t *sendbuf, size_t send_size,
- uint8_t *recvbuf, size_t *recv_len)
-{
- if (tis_senddata(sendbuf, send_size)) {
- printf("%s:%d failed sending data to TPM\n",
- __FILE__, __LINE__);
- return TPM_DRIVER_ERR;
- }
-
- return tis_readresponse(recvbuf, recv_len);
-}
diff --git a/src/drivers/pc80/tpm/Kconfig b/src/drivers/pc80/tpm/Kconfig
new file mode 100644
index 0000000..fbb3b42
--- /dev/null
+++ b/src/drivers/pc80/tpm/Kconfig
@@ -0,0 +1,7 @@
+config LPC_TPM
+ bool
+ default n
+ help
+ Enable this option to enable LPC TPM support in coreboot.
+
+ If unsure, say N.
diff --git a/src/drivers/pc80/tpm/Makefile.inc b/src/drivers/pc80/tpm/Makefile.inc
new file mode 100644
index 0000000..6f973e1
--- /dev/null
+++ b/src/drivers/pc80/tpm/Makefile.inc
@@ -0,0 +1 @@
+romstage-$(CONFIG_LPC_TPM) += tpm.c
diff --git a/src/drivers/pc80/tpm/tpm.c b/src/drivers/pc80/tpm/tpm.c
new file mode 100644
index 0000000..c0429dd
--- /dev/null
+++ b/src/drivers/pc80/tpm/tpm.c
@@ -0,0 +1,675 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. 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
+ */
+
+/*
+ * The code in this file has been heavily based on the article "Writing a TPM
+ * Device Driver" published on http://ptgmedia.pearsoncmg.com and the
+ * submission by Stefan Berger on Qemu-devel mailing list.
+ *
+ * One principal difference is that in the simplest config the other than 0
+ * TPM localities do not get mapped by some devices (for instance, by
+ * Infineon slb9635), so this driver provides access to locality 0 only.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <delay.h>
+#include <arch/io.h>
+#include <arch/byteorder.h>
+#include <console/console.h>
+#include <tpm.h>
+#include <arch/early_variables.h>
+
+#define PREFIX "lpc_tpm: "
+
+/* coreboot wrapper for TPM driver (start) */
+#define TPM_DEBUG(fmt, args...) \
+ if (CONFIG_DEBUG_TPM) { \
+ printk(BIOS_DEBUG, PREFIX); \
+ printk(BIOS_DEBUG, fmt , ##args); \
+ }
+#define TPM_DEBUG_IO_READ(reg_, val_) \
+ TPM_DEBUG("Read reg 0x%x returns 0x%x\n", (reg_), (val_))
+#define TPM_DEBUG_IO_WRITE(reg_, val_) \
+ TPM_DEBUG("Write reg 0x%x with 0x%x\n", (reg_), (val_))
+#define printf(x...) printk(BIOS_ERR, x)
+
+#define readb(_a) (*(volatile unsigned char *) (_a))
+#define writeb(_v, _a) (*(volatile unsigned char *) (_a) = (_v))
+#define readl(_a) (*(volatile unsigned long *) (_a))
+#define writel(_v, _a) (*(volatile unsigned long *) (_a) = (_v))
+/* coreboot wrapper for TPM driver (end) */
+
+#ifndef CONFIG_TPM_TIS_BASE_ADDRESS
+/* Base TPM address standard for x86 systems */
+#define CONFIG_TPM_TIS_BASE_ADDRESS 0xfed40000
+#endif
+
+/* the macro accepts the locality value, but only locality 0 is operational */
+#define TIS_REG(LOCALITY, REG) \
+ (void *)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG)
+
+/* hardware registers' offsets */
+#define TIS_REG_ACCESS 0x0
+#define TIS_REG_INT_ENABLE 0x8
+#define TIS_REG_INT_VECTOR 0xc
+#define TIS_REG_INT_STATUS 0x10
+#define TIS_REG_INTF_CAPABILITY 0x14
+#define TIS_REG_STS 0x18
+#define TIS_REG_BURST_COUNT 0x19
+#define TIS_REG_DATA_FIFO 0x24
+#define TIS_REG_DID_VID 0xf00
+#define TIS_REG_RID 0xf04
+
+/* Some registers' bit field definitions */
+#define TIS_STS_VALID (1 << 7) /* 0x80 */
+#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
+#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
+#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
+#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
+#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
+
+#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
+#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
+#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
+#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
+#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
+#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
+#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
+
+/*
+ * Error value returned if a tpm register does not enter the expected state
+ * after continuous polling. No actual TPM register reading ever returns ~0,
+ * so this value is a safe error indication to be mixed with possible status
+ * register values.
+ */
+#define TPM_TIMEOUT_ERR (~0)
+
+/* Error value returned on various TPM driver errors */
+#define TPM_DRIVER_ERR (~0)
+
+ /* 1 second is plenty for anything TPM does.*/
+#define MAX_DELAY_US (1000 * 1000)
+
+/*
+ * Structures defined below allow creating descriptions of TPM vendor/device
+ * ID information for run time discovery. The only device the system knows
+ * about at this time is Infineon slb9635
+ */
+struct device_name {
+ u16 dev_id;
+ const char * const dev_name;
+};
+
+struct vendor_name {
+ u16 vendor_id;
+ const char * vendor_name;
+ const struct device_name* dev_names;
+};
+
+static const struct device_name atmel_devices[] = {
+ {0x3204, "AT97SC3204"},
+ {0xffff}
+};
+
+static const struct device_name infineon_devices[] = {
+ {0x000b, "SLB9635 TT 1.2"},
+ {0xffff}
+};
+
+static const struct device_name nuvoton_devices[] = {
+ {0x00fe, "NPCT420AA V2"},
+ {0xffff}
+};
+
+static const struct device_name stmicro_devices[] = {
+ {0x0000, "ST33ZP24" },
+ {0xffff}
+};
+
+static const struct vendor_name vendor_names[] = {
+ {0x1114, "Atmel", atmel_devices},
+ {0x15d1, "Infineon", infineon_devices},
+ {0x1050, "Nuvoton", nuvoton_devices},
+ {0x104a, "ST Microelectronics", stmicro_devices},
+};
+
+/*
+ * Cached vendor/device ID pair to indicate that the device has been already
+ * discovered
+ */
+static u32 vendor_dev_id CAR_GLOBAL;
+
+static inline u8 tpm_read_status(int locality)
+{
+ u8 value = readb(TIS_REG(locality, TIS_REG_STS));
+ TPM_DEBUG_IO_READ(TIS_REG_STS, value);
+ return value;
+}
+
+static inline void tpm_write_status(u8 sts, int locality)
+{
+ TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts);
+ writeb(sts, TIS_REG(locality, TIS_REG_STS));
+}
+
+static inline u8 tpm_read_data(int locality)
+{
+ u8 value = readb(TIS_REG(locality, TIS_REG_DATA_FIFO));
+ TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value);
+ return value;
+}
+
+static inline void tpm_write_data(u8 data, int locality)
+{
+ TPM_DEBUG_IO_WRITE(TIS_REG_STS, data);
+ writeb(data, TIS_REG(locality, TIS_REG_DATA_FIFO));
+}
+
+static inline u16 tpm_read_burst_count(int locality)
+{
+ u16 count;
+ count = readb(TIS_REG(locality, TIS_REG_BURST_COUNT));
+ count |= readb(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8;
+ TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count);
+ return count;
+}
+
+static inline u8 tpm_read_access(int locality)
+{
+ u8 value = readb(TIS_REG(locality, TIS_REG_ACCESS));
+ TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value);
+ return value;
+}
+
+static inline void tpm_write_access(u8 data, int locality)
+{
+ TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data);
+ writeb(data, TIS_REG(locality, TIS_REG_ACCESS));
+}
+
+static inline u32 tpm_read_did_vid(int locality)
+{
+ u32 value = readl(TIS_REG(locality, TIS_REG_DID_VID));
+ TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value);
+ return value;
+}
+
+/*
+ * tis_wait_sts()
+ *
+ * Wait for at least a second for a status to change its state to match the
+ * expected state. Normally the transition happens within microseconds.
+ *
+ * @locality - locality
+ * @mask - bitmask for the bitfield(s) to watch
+ * @expected - value the field(s) are supposed to be set to
+ *
+ * Returns 0 on success or TPM_TIMEOUT_ERR on timeout.
+ */
+static int tis_wait_sts(int locality, u8 mask, u8 expected)
+{
+ u32 time_us = MAX_DELAY_US;
+ while (time_us > 0) {
+ u8 value = tpm_read_status(locality);
+ if ((value & mask) == expected)
+ return 0;
+ udelay(1); /* 1 us */
+ time_us--;
+ }
+ return TPM_TIMEOUT_ERR;
+}
+
+static inline int tis_wait_ready(int locality)
+{
+ return tis_wait_sts(locality, TIS_STS_COMMAND_READY,
+ TIS_STS_COMMAND_READY);
+}
+
+static inline int tis_wait_valid(int locality)
+{
+ return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID);
+}
+
+static inline int tis_wait_valid_data(int locality)
+{
+ const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
+ return tis_wait_sts(locality, has_data, has_data);
+}
+
+static inline int tis_has_valid_data(int locality)
+{
+ const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
+ return (tpm_read_status(locality) & has_data) == has_data;
+}
+
+static inline int tis_expect_data(int locality)
+{
+ return !!(tpm_read_status(locality) & TIS_STS_EXPECT);
+}
+
+/*
+ * tis_wait_access()
+ *
+ * Wait for at least a second for a access to change its state to match the
+ * expected state. Normally the transition happens within microseconds.
+ *
+ * @locality - locality
+ * @mask - bitmask for the bitfield(s) to watch
+ * @expected - value the field(s) are supposed to be set to
+ *
+ * Returns 0 on success or TPM_TIMEOUT_ERR on timeout.
+ */
+static int tis_wait_access(int locality, u8 mask, u8 expected)
+{
+ u32 time_us = MAX_DELAY_US;
+ while (time_us > 0) {
+ u8 value = tpm_read_access(locality);
+ if ((value & mask) == expected)
+ return 0;
+ udelay(1); /* 1 us */
+ time_us--;
+ }
+ return TPM_TIMEOUT_ERR;
+}
+
+static inline int tis_wait_dropped_access(int locality)
+{
+ return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY, 0);
+}
+
+static inline int tis_wait_received_access(int locality)
+{
+ return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY,
+ TIS_ACCESS_ACTIVE_LOCALITY);
+}
+
+static inline int tis_has_access(int locality)
+{
+ return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY);
+}
+
+static inline void tis_request_access(int locality)
+{
+ tpm_write_access(TIS_ACCESS_REQUEST_USE, locality);
+}
+
+static inline void tis_drop_access(int locality)
+{
+ tpm_write_access(TIS_ACCESS_ACTIVE_LOCALITY, locality);
+}
+
+/*
+ * PC Client Specific TPM Interface Specification section 11.2.12:
+ *
+ * Software must be prepared to send two writes of a "1" to command ready
+ * field: the first to indicate successful read of all the data, thus
+ * clearing the data from the ReadFIFO and freeing the TPM's resources,
+ * and the second to indicate to the TPM it is about to send a new command.
+ *
+ * In practice not all TPMs behave the same so it is necessary to be
+ * flexible when trying to set command ready.
+ *
+ * Returns 0 on success if the TPM is ready for transactions.
+ * Returns TPM_TIMEOUT_ERR if the command ready bit does not get set.
+ */
+static int tis_command_ready(u8 locality)
+{
+ u32 status;
+
+ /* 1st attempt to set command ready */
+ tpm_write_status(TIS_STS_COMMAND_READY, locality);
+
+ /* Wait for response */
+ status = tpm_read_status(locality);
+
+ /* Check if command ready is set yet */
+ if (status & TIS_STS_COMMAND_READY)
+ return 0;
+
+ /* 2nd attempt to set command ready */
+ tpm_write_status(TIS_STS_COMMAND_READY, locality);
+
+ return tis_wait_ready(locality);
+}
+
+/*
+ * Probe the TPM device and try determining its manufacturer/device name.
+ *
+ * Returns 0 on success (the device is found or was found during an earlier
+ * invocation) or TPM_DRIVER_ERR if the device is not found.
+ */
+static u32 tis_probe(void)
+{
+ const char *device_name = "unknown";
+ const char *vendor_name = device_name;
+ const struct device_name *dev;
+ u32 didvid;
+ u16 vid, did;
+ int i;
+
+ if (car_get_var(vendor_dev_id))
+ return 0; /* Already probed. */
+
+ didvid = tpm_read_did_vid(0);
+ if (!didvid || (didvid == 0xffffffff)) {
+ printf("%s: No TPM device found\n", __FUNCTION__);
+ return TPM_DRIVER_ERR;
+ }
+
+ car_set_var(vendor_dev_id, didvid);
+
+ vid = didvid & 0xffff;
+ did = (didvid >> 16) & 0xffff;
+ for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
+ int j = 0;
+ u16 known_did;
+ if (vid == vendor_names[i].vendor_id) {
+ vendor_name = vendor_names[i].vendor_name;
+ } else {
+ continue;
+ }
+ dev = &vendor_names[i].dev_names[j];
+ while ((known_did = dev->dev_id) != 0xffff) {
+ if (known_did == did) {
+ device_name = dev->dev_name;
+ break;
+ }
+ j++;
+ }
+ break;
+ }
+ /* this will have to be converted into debug printout */
+ printf("Found TPM %s by %s\n", device_name, vendor_name);
+ return 0;
+}
+
+/*
+ * tis_senddata()
+ *
+ * send the passed in data to the TPM device.
+ *
+ * @data - address of the data to send, byte by byte
+ * @len - length of the data to send
+ *
+ * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
+ * not accept the entire command).
+ */
+static u32 tis_senddata(const u8 * const data, u32 len)
+{
+ u32 offset = 0;
+ u16 burst = 0;
+ u32 max_cycles = 0;
+ u8 locality = 0;
+
+ if (tis_wait_ready(locality)) {
+ printf("%s:%d - failed to get 'command_ready' status\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+ burst = tpm_read_burst_count(locality);
+
+ while (1) {
+ unsigned count;
+
+ /* Wait till the device is ready to accept more data. */
+ while (!burst) {
+ if (max_cycles++ == MAX_DELAY_US) {
+ printf("%s:%d failed to feed %d bytes of %d\n",
+ __FILE__, __LINE__, len - offset, len);
+ return TPM_DRIVER_ERR;
+ }
+ udelay(1);
+ burst = tpm_read_burst_count(locality);
+ }
+
+ max_cycles = 0;
+
+ /*
+ * Calculate number of bytes the TPM is ready to accept in one
+ * shot.
+ *
+ * We want to send the last byte outside of the loop (hence
+ * the -1 below) to make sure that the 'expected' status bit
+ * changes to zero exactly after the last byte is fed into the
+ * FIFO.
+ */
+ count = min(burst, len - offset - 1);
+ while (count--)
+ tpm_write_data(data[offset++], locality);
+
+ if (tis_wait_valid(locality) || !tis_expect_data(locality)) {
+ printf("%s:%d TPM command feed overflow\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ burst = tpm_read_burst_count(locality);
+ if ((offset == (len - 1)) && burst)
+ /*
+ * We need to be able to send the last byte to the
+ * device, so burst size must be nonzero before we
+ * break out.
+ */
+ break;
+ }
+
+ /* Send the last byte. */
+ tpm_write_data(data[offset++], locality);
+
+ /*
+ * Verify that TPM does not expect any more data as part of this
+ * command.
+ */
+ if (tis_wait_valid(locality) || tis_expect_data(locality)) {
+ printf("%s:%d unexpected TPM status 0x%x\n",
+ __FILE__, __LINE__, tpm_read_status(locality));
+ return TPM_DRIVER_ERR;
+ }
+
+ /* OK, sitting pretty, let's start the command execution. */
+ tpm_write_status(TIS_STS_TPM_GO, locality);
+
+ return 0;
+}
+
+/*
+ * tis_readresponse()
+ *
+ * read the TPM device response after a command was issued.
+ *
+ * @buffer - address where to read the response, byte by byte.
+ * @len - pointer to the size of buffer
+ *
+ * On success stores the number of received bytes to len and returns 0. On
+ * errors (misformatted TPM data or synchronization problems) returns
+ * TPM_DRIVER_ERR.
+ */
+static u32 tis_readresponse(u8 *buffer, size_t *len)
+{
+ u16 burst_count;
+ u32 offset = 0;
+ u8 locality = 0;
+ u32 expected_count = *len;
+ int max_cycles = 0;
+
+ /* Wait for the TPM to process the command */
+ if (tis_wait_valid_data(locality)) {
+ printf("%s:%d failed processing command\n", __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ do {
+ while ((burst_count = tpm_read_burst_count(locality)) == 0) {
+ if (max_cycles++ == MAX_DELAY_US) {
+ printf("%s:%d TPM stuck on read\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+ udelay(1);
+ }
+
+ max_cycles = 0;
+
+ while (burst_count-- && (offset < expected_count)) {
+ buffer[offset++] = tpm_read_data(locality);
+ if (offset == 6) {
+ /*
+ * We got the first six bytes of the reply,
+ * let's figure out how many bytes to expect
+ * total - it is stored as a 4 byte number in
+ * network order, starting with offset 2 into
+ * the body of the reply.
+ */
+ u32 real_length;
+ memcpy(&real_length,
+ buffer + 2,
+ sizeof(real_length));
+ expected_count = be32_to_cpu(real_length);
+
+ if ((expected_count < offset) ||
+ (expected_count > *len)) {
+ printf("%s:%d bad response size %d\n",
+ __FILE__, __LINE__,
+ expected_count);
+ return TPM_DRIVER_ERR;
+ }
+ }
+ }
+
+ /* Wait for the next portion */
+ if (tis_wait_valid(locality)) {
+ printf("%s:%d failed to read response\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ if (offset == expected_count)
+ break; /* We got all we need */
+
+ } while (tis_has_valid_data(locality));
+
+ /* * Make sure we indeed read all there was. */
+ if (tis_has_valid_data(locality)) {
+ printf("%s:%d wrong receive status: %x %d bytes left\n",
+ __FILE__, __LINE__, tpm_read_status(locality),
+ tpm_read_burst_count(locality));
+ return TPM_DRIVER_ERR;
+ }
+
+ /* Tell the TPM that we are done. */
+ if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
+ return TPM_DRIVER_ERR;
+
+ *len = offset;
+ return 0;
+}
+
+/*
+ * tis_init()
+ *
+ * Initialize the TPM device. Returns 0 on success or TPM_DRIVER_ERR on
+ * failure (in case device probing did not succeed).
+ */
+int tis_init(void)
+{
+ if (tis_probe())
+ return TPM_DRIVER_ERR;
+ return 0;
+}
+
+/*
+ * 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, TPM_DRIVER_ERR on failure.
+ */
+int tis_open(void)
+{
+ u8 locality = 0; /* we use locality zero for everything */
+
+ if (tis_close())
+ return TPM_DRIVER_ERR;
+
+ /* now request access to locality */
+ tis_request_access(locality);
+
+ /* did we get a lock? */
+ if (tis_wait_received_access(locality)) {
+ printf("%s:%d - failed to lock locality %d\n",
+ __FILE__, __LINE__, locality);
+ return TPM_DRIVER_ERR;
+ }
+
+ /* Certain TPMs seem to need some delay here or they hang... */
+ udelay(10);
+
+ if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
+ return TPM_DRIVER_ERR;
+
+ return 0;
+}
+
+/*
+ * tis_close()
+ *
+ * terminate the current session with the TPM by releasing the locked
+ * locality. Returns 0 on success of TPM_DRIVER_ERR on failure (in case lock
+ * removal did not succeed).
+ */
+int tis_close(void)
+{
+ u8 locality = 0;
+ if (tis_has_access(locality)) {
+ tis_drop_access(locality);
+ if (tis_wait_dropped_access(locality)) {
+ printf("%s:%d - failed to release locality %d\n",
+ __FILE__, __LINE__, locality);
+ return TPM_DRIVER_ERR;
+ }
+ }
+ return 0;
+}
+
+/*
+ * 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 TPM_DRIVER_ERR on failure.
+ */
+int tis_sendrecv(const uint8_t *sendbuf, size_t send_size,
+ uint8_t *recvbuf, size_t *recv_len)
+{
+ if (tis_senddata(sendbuf, send_size)) {
+ printf("%s:%d failed sending data to TPM\n",
+ __FILE__, __LINE__);
+ return TPM_DRIVER_ERR;
+ }
+
+ return tis_readresponse(recvbuf, recv_len);
+}
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9461
-gerrit
commit 3278dbaef36757d5c2c80eeb533dc52599daae55
Author: Duncan Laurie <dlaurie(a)chromium.org>
Date: Tue Oct 7 15:19:54 2014 -0700
broadwell: Skip DDI-A enable in S3 resume
DDI-A should not need re-enabled in the resume path, just
the resume path when we did not execute VBIOS.
BUG=chrome-os-partner:28234
BRANCH=samus,auron
TEST=build and boot on samus, test suspend+resume
Change-Id: I29d67591ac903bc1d712a956462bcf4a764ef2eb
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: c3fbeac10f3834a6d848154aa3449672871b13df
Original-Change-Id: Iaf7d083c5c92c42b7a117e2d2c9546ada6bf5f76
Original-Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/221988
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/soc/intel/broadwell/igd.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/soc/intel/broadwell/igd.c b/src/soc/intel/broadwell/igd.c
index 714a139..d8b51a3 100644
--- a/src/soc/intel/broadwell/igd.c
+++ b/src/soc/intel/broadwell/igd.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <arch/acpi.h>
#include <arch/io.h>
#include <bootmode.h>
#include <console/console.h>
@@ -522,7 +523,7 @@ static void igd_init(struct device *dev)
reg_script_run_on_dev(dev, haswell_late_init_script);
}
- if (!gfx_get_init_done()) {
+ if (!gfx_get_init_done() && acpi_slp_type != 3) {
/*
* Enable DDI-A if the Option ROM did not execute:
*
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9460
-gerrit
commit 1131d499ae185a002161c26521d294b0ce160a7c
Author: Duncan Laurie <dlaurie(a)chromium.org>
Date: Mon Sep 29 08:51:45 2014 -0700
broadwell: Add support for ACPI \_GPE._SWS
In order to report the GPE that woke the system to the kernel
coreboot needs to keep track of the first GPE wake source and
save it in NVS so it can be returned in \_GPE._SWS method.
This is similar to the saving of PM1 status but needs to go
through all the GPE0_STS registers and check for enabled and
triggered events.
A bit of cleanup is done for areas that were touched:
- platform.asl was not formatted correctly
BUG=chrome-os-partner:8127
BRANCH=samus,auron
TEST=manual:
- suspend/resume and wake from EC event like keyboard:
ACPI _SWS is PM1 Index -1 GPE Index 112 ("special" GPIO27)
- suspend/resume and wake from RTC event:
ACPI _SWS is PM1 Index 10 GPE Index -1 (RTC)
- suspend/resume and wake from power button:
ACPI _SWS is PM1 Index 8 GPE Index -1
- suspend/resume and wake from touchpad:
ACPI _SWS is PM1 Index -1 GPE Index 13
- suspend/resume and wake from WLAN:
ACPI _SWS is PM1 Index -1 GPE Index 10
Change-Id: I574f8cd83c8bb42f420e1a00e71a23aa23195f53
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: d4e06c7dfc73f2952ce8f81263e316980aa9760f
Original-Change-Id: I9bfbbe4385f2acc2a50f41ae321b4bae262b7078
Original-Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/220324
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/soc/intel/broadwell/acpi/globalnvs.asl | 1 +
src/soc/intel/broadwell/acpi/platform.asl | 35 ++++++++++++++++++++----------
src/soc/intel/broadwell/include/soc/nvs.h | 3 ++-
src/soc/intel/broadwell/include/soc/pm.h | 2 ++
src/soc/intel/broadwell/ramstage.c | 33 +++++++++++++++++++++++-----
5 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/src/soc/intel/broadwell/acpi/globalnvs.asl b/src/soc/intel/broadwell/acpi/globalnvs.asl
index d8b75ac..4ba384a 100644
--- a/src/soc/intel/broadwell/acpi/globalnvs.asl
+++ b/src/soc/intel/broadwell/acpi/globalnvs.asl
@@ -62,6 +62,7 @@ Field (GNVS, ByteAcc, NoLock, Preserve)
CMEM, 32, // 0x19 - 0x1c - CBMEM TOC
CBMC, 32, // 0x1d - 0x20 - Coreboot Memory Console
PM1I, 32, // 0x21 - 0x24 - PM1 wake status bit
+ GPEI, 32, // 0x25 - 0x28 - GPE wake status bit
/* ChromeOS specific */
Offset (0x100),
diff --git a/src/soc/intel/broadwell/acpi/platform.asl b/src/soc/intel/broadwell/acpi/platform.asl
index 2361fe8..f63168a 100644
--- a/src/soc/intel/broadwell/acpi/platform.asl
+++ b/src/soc/intel/broadwell/acpi/platform.asl
@@ -36,7 +36,7 @@ Field (POST, ByteAcc, Lock, Preserve)
}
/* SMI I/O Trap */
-Method(TRAP, 1, Serialized)
+Method (TRAP, 1, Serialized)
{
Store (Arg0, SMIF) // SMI Function
Store (0, TRP0) // Generate trap
@@ -50,29 +50,42 @@ Method(TRAP, 1, Serialized)
* with a parameter of 1 for Local Apic/IOAPIC configuration.
*/
-Method(_PIC, 1)
+Method (_PIC, 1)
{
- // Remember the OS' IRQ routing choice.
- Store(Arg0, PICM)
+ /* Remember the OS' IRQ routing choice. */
+ Store (Arg0, PICM)
}
-/* The _PTS method (Prepare To Sleep) is called before the OS is
+/*
+ * The _PTS method (Prepare To Sleep) is called before the OS is
* entering a sleep state. The sleep state number is passed in Arg0
*/
-Method(_PTS,1)
+Method (_PTS, 1)
{
}
/* The _WAK method is called on system wakeup */
-Method(_WAK,1)
+Method (_WAK, 1)
+{
+ Return (Package (){ 0, 0 })
+}
+
+Scope (\_SB)
{
- Return(Package(){0,0})
+ Method (_SWS)
+ {
+ /* Index into PM1 for device that caused wake */
+ Return (\PM1I)
+ }
}
-Method (_SWS)
+Scope (\_GPE)
{
- /* Index into PM1 for device that caused wake */
- Return (\PM1I)
+ Method (_SWS)
+ {
+ /* Index into GPE for device that caused wake */
+ Return (\GPEI)
+ }
}
diff --git a/src/soc/intel/broadwell/include/soc/nvs.h b/src/soc/intel/broadwell/include/soc/nvs.h
index 41b2e4a..195dd43 100644
--- a/src/soc/intel/broadwell/include/soc/nvs.h
+++ b/src/soc/intel/broadwell/include/soc/nvs.h
@@ -53,7 +53,8 @@ typedef struct {
u32 obsolete_cmem; /* 0x19 - 0x1c - CBMEM TOC */
u32 cbmc; /* 0x1d - 0x20 - Coreboot Memory Console */
u32 pm1i; /* 0x21 - 0x24 - PM1 wake status bit */
- u8 rsvd3[219];
+ u32 gpei; /* 0x25 - 0x28 - GPE wake status bit */
+ u8 rsvd3[215];
/* ChromeOS specific (0x100 - 0xfff) */
chromeos_acpi_t chromeos;
diff --git a/src/soc/intel/broadwell/include/soc/pm.h b/src/soc/intel/broadwell/include/soc/pm.h
index 6bc98ef..54fc4a7 100644
--- a/src/soc/intel/broadwell/include/soc/pm.h
+++ b/src/soc/intel/broadwell/include/soc/pm.h
@@ -83,6 +83,8 @@
#define TCO2_STS 0x66
#define TCO2_STS_SECOND_TO (1 << 1)
+#define GPE0_REG_MAX 4
+#define GPE0_REG_SIZE 32
#define GPE0_STS(x) (0x80 + (x * 4))
#define GPE_31_0 0 /* 0x80/0x90 = GPE[31:0] */
#define GPE_63_32 1 /* 0x84/0x94 = GPE[63:32] */
diff --git a/src/soc/intel/broadwell/ramstage.c b/src/soc/intel/broadwell/ramstage.c
index beabded..c9a422f 100644
--- a/src/soc/intel/broadwell/ramstage.c
+++ b/src/soc/intel/broadwell/ramstage.c
@@ -28,11 +28,12 @@
#include <soc/ramstage.h>
#include <soc/intel/broadwell/chip.h>
-/* Save bit index for first enabled event in PM1_STS for \_SB._SWS */
-static void s3_save_acpi_wake_source(global_nvs_t *gnvs)
+/* Save bit index for PM1_STS and GPE_STS for ACPI _SWS */
+static void save_acpi_wake_source(global_nvs_t *gnvs)
{
struct chipset_power_state *ps = cbmem_find(CBMEM_ID_POWER_STATE);
uint16_t pm1;
+ int gpe_reg;
if (!ps)
return;
@@ -50,8 +51,30 @@ static void s3_save_acpi_wake_source(global_nvs_t *gnvs)
if (gnvs->pm1i >= 16)
gnvs->pm1i = -1;
- printk(BIOS_DEBUG, "ACPI System Wake Source is PM1 Index %d\n",
- gnvs->pm1i);
+ /* Scan for first set bit in GPE registers */
+ for (gpe_reg = 0; gpe_reg < GPE0_REG_MAX; gpe_reg++) {
+ u32 gpe = ps->gpe0_sts[gpe_reg] & ps->gpe0_en[gpe_reg];
+ int start = gpe_reg * GPE0_REG_SIZE;
+ int end = start + GPE0_REG_SIZE;
+
+ if (gpe == 0) {
+ gnvs->gpei = end;
+ continue;
+ }
+
+ for (gnvs->gpei = start; gnvs->gpei < end; gnvs->gpei++) {
+ if (gpe & 1)
+ break;
+ gpe >>= 1;
+ }
+ }
+
+ /* If unable to determine then return -1 */
+ if (gnvs->gpei >= (GPE0_REG_MAX * GPE0_REG_SIZE))
+ gnvs->gpei = -1;
+
+ printk(BIOS_DEBUG, "ACPI _SWS is PM1 Index %d GPE Index %d\n",
+ gnvs->pm1i, gnvs->gpei);
}
static void s3_resume_prepare(void)
@@ -65,7 +88,7 @@ static void s3_resume_prepare(void)
if (!acpi_is_wakeup_s3())
memset(gnvs, 0, sizeof(global_nvs_t));
else
- s3_save_acpi_wake_source(gnvs);
+ save_acpi_wake_source(gnvs);
}
void broadwell_init_pre_device(void *chip_info)