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 ad622c2466a76fb298930934358a31c660ef4823
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/9468
-gerrit
commit 2924225bdf710d9bd3216fdc0b599c7d75007b16
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/9467
-gerrit
commit 9352fc86c31a7e3ee63e8e7f418dfe03b4172f6e
Author: Chiranjeevi Rapolu <chiranjeevi.rapolu(a)intel.com>
Date: Wed Oct 29 17:26:05 2014 -0700
broadwell: Increase I2C SDA hold timing to 300ns
I2C bus SDA hold time can be marginal with 60ns value, especially
when there is level shifter on the bus. So program it to 300ns
based on Fast-mode specification, which is between 0 to 900ns.
Apply the same timing for Standard-mode as well.
Refer to original bug on BayTrail chrome-os-partner:28092, this
is to carry forward the fix to Broadwell.
BRANCH=chromeos-2013.04
BUG=chrome-os-partner:33378
TEST=suspend resume test, watch for I2C errors
Change-Id: I93200b141602163903f5c9f52b94013bcf3382a5
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: 72b82a1d5d836594e7d0f95972cc0dc91ae7ff8c
Original-Change-Id: I995d6868a44f2578a6d0b18dd5e8548f3c3cd494
Original-Signed-off-by: Chiranjeevi Rapolu <chiranjeevi.rapolu(a)intel.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/226386
Original-Reviewed-by: Wenkai Du <wenkai.du(a)intel.com>
Original-Reviewed-by: Duncan Laurie <dlaurie(a)chromium.org>
---
src/soc/intel/broadwell/acpi/serialio.asl | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/soc/intel/broadwell/acpi/serialio.asl b/src/soc/intel/broadwell/acpi/serialio.asl
index 6b42d54..7ffc671 100644
--- a/src/soc/intel/broadwell/acpi/serialio.asl
+++ b/src/soc/intel/broadwell/acpi/serialio.asl
@@ -205,8 +205,8 @@ Device (I2C0)
Name (_UID, 1)
Name (_ADR, 0x00150001)
- Name (SSCN, Package () { 432, 507, 9 })
- Name (FMCN, Package () { 72, 160, 9 })
+ Name (SSCN, Package () { 432, 507, 30 })
+ Name (FMCN, Package () { 72, 160, 30 })
// BAR0 is assigned during PCI enumeration and saved into NVS
Name (RBUF, ResourceTemplate ()
@@ -276,8 +276,8 @@ Device (I2C1)
Name (_UID, 1)
Name (_ADR, 0x00150002)
- Name (SSCN, Package () { 432, 507, 9 })
- Name (FMCN, Package () { 72, 160, 9 })
+ Name (SSCN, Package () { 432, 507, 30 })
+ Name (FMCN, Package () { 72, 160, 30 })
// BAR0 is assigned during PCI enumeration and saved into NVS
Name (RBUF, ResourceTemplate ()
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9466
-gerrit
commit 3f567b6b4cfe28ab7adb0669b0ef25d7c2b3fc2e
Author: Vadim Bendebury <vbendeb(a)chromium.org>
Date: Tue Oct 28 18:23:28 2014 -0700
urara: support building with CHROMEOS enabled
Chrome OS support needs to be enabled on urara. This patch adds a
placeholder file to keep Chrome OS support code.
BRANCH=none
BUG=chrome-os-partner:31438
TEST=none
Change-Id: I0731469934f04bd68914f09db5d64758c5d01545
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: 169c62c9443c3b9fcab23b312b5cb18ba79437f4
Original-Change-Id: I8ec328d4f965ff80d17847f2f8ce62b402c42a46
Original-Signed-off-by: Vadim Bendebury <vbendeb(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/226179
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/mainboard/google/urara/Kconfig | 1 +
src/mainboard/google/urara/Makefile.inc | 1 +
src/mainboard/google/urara/chromeos.c | 28 ++++++++++++++++++++++++++++
3 files changed, 30 insertions(+)
diff --git a/src/mainboard/google/urara/Kconfig b/src/mainboard/google/urara/Kconfig
index 26add60..1386bc8 100644
--- a/src/mainboard/google/urara/Kconfig
+++ b/src/mainboard/google/urara/Kconfig
@@ -25,6 +25,7 @@ config BOARD_SPECIFIC_OPTIONS
def_bool y
select BOARD_ROMSIZE_KB_512
select BOOTBLOCK_CONSOLE
+ select MAINBOARD_HAS_CHROMEOS
select CONFIG_SPI_FLASH_WINBOND
select CPU_IMGTEC_PISTACHIO
select COMMON_CBFS_SPI_WRAPPER
diff --git a/src/mainboard/google/urara/Makefile.inc b/src/mainboard/google/urara/Makefile.inc
index 5a9dc02..2835a4b 100644
--- a/src/mainboard/google/urara/Makefile.inc
+++ b/src/mainboard/google/urara/Makefile.inc
@@ -19,6 +19,7 @@
# MA 02110-1301 USA
#
+ramstage-$(CONFIG_CHROMEOS) += chromeos.c
ramstage-y += mainboard.c
bootblock-y += memlayout.ld
diff --git a/src/mainboard/google/urara/chromeos.c b/src/mainboard/google/urara/chromeos.c
new file mode 100644
index 0000000..cc8c79b
--- /dev/null
+++ b/src/mainboard/google/urara/chromeos.c
@@ -0,0 +1,28 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Technologies
+ *
+ * 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
+ */
+
+#include <boot/coreboot_tables.h>
+#include <console/console.h>
+#include <gpio.h>
+
+
+void fill_lb_gpios(struct lb_gpios *gpios)
+{
+ printk(BIOS_ERR, "%s unsupported, but called\n", __func__);
+}
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9465
-gerrit
commit d0cb994922e5917038e66dfcf7f7ee2f85a81aa8
Author: Vadim Bendebury <vbendeb(a)chromium.org>
Date: Wed Oct 22 12:14:29 2014 -0700
storm: do not enable the ethernet switch by default
The ethernet switch, as soon as it is taken out of reset comes up in
default (bridging) mode, which allows traffic to flow freely across
the ports.
Let's keep it in reset such that there is no cross port traffic
happening while the device boots up.
BRANCH=storm
BUG=chrome-os-partner:32646
TEST=verified that the switch is held in reset during boot.
Change-Id: Ia1dbb47d892d564145da17425a596bf9bad40d29
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: 50551d8c9a44d1b63e0948070f6573adf7729d37
Original-Change-Id: I6bf698beddc98ce18fee6b3b39622e356c8cfbad
Original-Signed-off-by: Vadim Bendebury <vbendeb(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/224989
Original-Reviewed-by: Toshi Kikuchi <toshik(a)chromium.org>
---
src/mainboard/google/storm/mainboard.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/src/mainboard/google/storm/mainboard.c b/src/mainboard/google/storm/mainboard.c
index acadce5..faf296a 100644
--- a/src/mainboard/google/storm/mainboard.c
+++ b/src/mainboard/google/storm/mainboard.c
@@ -92,28 +92,27 @@ static void setup_tpm(void)
}
#define SW_RESET_GPIO 26
-static void deassert_sw_reset(void)
+static void assert_sw_reset(void)
{
if (board_id() == 0)
return;
/*
- * only proto0.2 and later care about this. This signal is eventually
- * driving the ehernet switch reset input, which is active low. But
- * since this signal gets inverted along the way, the GPIO needs to be
- * driven low to take the switch out of reset.
+ * only proto0.2 and later care about this. We want to keep the
+ * ethernet switch in reset, otherwise it comes up in default
+ * (bridging) mode.
*/
gpio_tlmm_config_set(SW_RESET_GPIO, FUNC_SEL_GPIO,
GPIO_PULL_UP, GPIO_4MA, GPIO_ENABLE);
- gpio_set_out_value(SW_RESET_GPIO, 0);
+ gpio_set_out_value(SW_RESET_GPIO, 1);
}
static void mainboard_init(device_t dev)
{
setup_mmu();
setup_usb();
- deassert_sw_reset();
+ assert_sw_reset();
setup_tpm();
}
Stefan Reinauer (stefan.reinauer(a)coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9464
-gerrit
commit 39a560037c11c779e46e69680003c648c5ec3be3
Author: Duncan Laurie <dlaurie(a)chromium.org>
Date: Thu Oct 9 16:13:24 2014 -0700
broadwell: Work around VBIOS framebuffer issue
The first 64 bytes of the framebuffer contain garbage after running
the option rom and after calling the VBE mode set with the flag to
clear the framebuffer.
Work around this issue by clearing the first 64 bytes in the framebuffer
in the broadwell graphics setup code after it executes the VBIOS.
BUG=chrome-os-partner:32771
BRANCH=samus,auron
TEST=build and boot on samus in dev mode, check for graphical corruption
Change-Id: I0381e32a5ea17e13c4ed598835999c12136418cf
Signed-off-by: Stefan Reinauer <reinauer(a)chromium.org>
Original-Commit-Id: f29c1b0b7c100cf290f82de671042823032f71c9
Original-Change-Id: I072bc913f7daea16e4861a7549e1b4ec85cde4cd
Original-Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/222676
Original-Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/soc/intel/broadwell/igd.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/soc/intel/broadwell/igd.c b/src/soc/intel/broadwell/igd.c
index d8b51a3..41a6961 100644
--- a/src/soc/intel/broadwell/igd.c
+++ b/src/soc/intel/broadwell/igd.c
@@ -523,6 +523,15 @@ static void igd_init(struct device *dev)
reg_script_run_on_dev(dev, haswell_late_init_script);
}
+ if (gfx_get_init_done()) {
+ /*
+ * Work around VBIOS issue that is not clearing first 64
+ * bytes of the framebuffer during VBE mode set.
+ */
+ struct resource *fb = find_resource(dev, PCI_BASE_ADDRESS_2);
+ memset((void *)((u32)fb->base), 0, 64);
+ }
+
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/9461
-gerrit
commit 17b5f83fb3ea703ca58ec29ecb75f034273fe83d
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 63453a2efb3a1a28c604a0504472081b98ed5e4b
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 | 31 ++++++++++++++++++++++----
5 files changed, 56 insertions(+), 16 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..158228f 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)