Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6519
-gerrit
commit 998ad31138545f34516479b3ddb1072a1ec18939
Author: Stefan Reinauer <reinauer(a)chromium.org>
Date: Thu Aug 29 16:05:02 2013 -0700
drivers: Add I2C TPM driver to coreboot
On ARM platforms the TPM is not attached through LPC but through I2C.
This patch adds an I2C TPM driver that supports the following chips:
* Infineon SLB9635
* Infineon SLB9645
In order to select the correct TPM implementation cleanly, CONFIG_TPM
is moved to src/Kconfig and does the correct choice.
Old-Change-Id: I2def0e0f86a869d6fcf56fc4ccab0bc935de2bf1
Signed-off-by: Stefan Reinauer <reinauer(a)google.com>
Reviewed-on: https://chromium-review.googlesource.com/167543
Reviewed-by: ron minnich <rminnich(a)chromium.org>
(cherry picked from commit b4049a0e96f6335a93877e1e884f9a440487c421)
i2c tpm: Remove mostly useless delay code/tables.
I assume from the code in the TPM driver that the TPM spec defines
different types of delays and timeouts which each have a particular
duration, and that the TPM can tell you how long each type is if you ask
it. There was a large table, some members of a data structure, and a
function or two which managed the timeouts and figured their value for
different operations. The timeout values for the various "ordinals"
were never set in the vendor specific data structure, however, and
always defaulted to 2 minutes. Similarly the timeouts a, b, c, and d
were never overridden from their defaults. This change gets rid of all
the timeout management code and makes the "ordinal" timeout 2 minutes
and the a, b, c, and d timeouts 2 seconds, the larger of the two default
values.
This is a port from depthcharge to coreboot, original change:
https://chromium-review.googlesource.com/#/c/168363/
Signed-off-by: Gabe Black <gabeblack(a)google.com>
Signed-off-by: Stefan Reinauer <reinauer(a)google.com>
Old-Change-Id: I79696d6329184ca07f6a1be4f6ca85e1655a7aaf
Reviewed-on: https://chromium-review.googlesource.com/168583
Reviewed-by: Gabe Black <gabeblack(a)chromium.org>
Tested-by: Stefan Reinauer <reinauer(a)google.com>
Commit-Queue: Stefan Reinauer <reinauer(a)google.com>
(cherry picked from commit b22395a73f361c38626911808332a3706b2334fe)
TPM: Stop requesting/releasing the TPM locality.
The locality is requested when the TPM is initialized and released when
it's cleaned up. There's no reason to set it to the same thing again and
restore it back to the same value before and after every transaction.
forward ported from https://chromium-review.googlesource.com/#/c/168400
Old-Change-Id: I291d1f86f220ef0eff6809c6cb00459bf95aa5e0
Signed-off-by: Gabe Black <gabeblack(a)google.com>
Signed-off-by: Stefan Reinauer <reinauer(a)google.com>
Reviewed-on: https://chromium-review.googlesource.com/168584
Reviewed-by: Gabe Black <gabeblack(a)chromium.org>
(cherry picked from commit cc866c20c6f936f349d2f1773dd492dca9bbf0c1)
Squashed three commits for the i2c tpm driver.
Change-Id: Ie7a50c50fda8ee986c02de7fe27551666998229d
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
src/Kconfig | 10 +
src/drivers/i2c/Kconfig | 1 +
src/drivers/i2c/Makefile.inc | 1 +
src/drivers/i2c/tpm/Kconfig | 9 +
src/drivers/i2c/tpm/Makefile.inc | 2 +
src/drivers/i2c/tpm/tis.h | 74 +++++
src/drivers/i2c/tpm/tis_i2c.c | 103 +++++++
src/drivers/i2c/tpm/tpm.c | 146 ++++++++++
src/drivers/i2c/tpm/tpm.h | 156 ++++++++++
src/drivers/i2c/tpm/tpm_tis_i2c.c | 578 ++++++++++++++++++++++++++++++++++++++
src/drivers/pc80/Kconfig | 2 +-
src/drivers/pc80/Makefile.inc | 2 +-
12 files changed, 1082 insertions(+), 2 deletions(-)
diff --git a/src/Kconfig b/src/Kconfig
index af82353..693e0e7 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -269,6 +269,16 @@ menu "Generic Drivers"
source src/drivers/Kconfig
endmenu
+config TPM
+ bool
+ default n
+ select LPC_TPM if ARCH_X86
+ select I2C_TPM if ARCH_ARMV7
+ help
+ Enable this option to enable TPM support in coreboot.
+
+ If unsure, say N.
+
config HEAP_SIZE
hex
default 0x4000
diff --git a/src/drivers/i2c/Kconfig b/src/drivers/i2c/Kconfig
index a6f59f4..5c55c44 100644
--- a/src/drivers/i2c/Kconfig
+++ b/src/drivers/i2c/Kconfig
@@ -5,5 +5,6 @@ source src/drivers/i2c/i2cmux/Kconfig
source src/drivers/i2c/i2cmux2/Kconfig
source src/drivers/i2c/lm63/Kconfig
source src/drivers/i2c/rtd2132/Kconfig
+source src/drivers/i2c/tpm/Kconfig
source src/drivers/i2c/w83795/Kconfig
source src/drivers/i2c/w83793/Kconfig
diff --git a/src/drivers/i2c/Makefile.inc b/src/drivers/i2c/Makefile.inc
index 8c61b6a..81ca409 100644
--- a/src/drivers/i2c/Makefile.inc
+++ b/src/drivers/i2c/Makefile.inc
@@ -5,6 +5,7 @@ subdirs-y += i2cmux
subdirs-y += i2cmux2
subdirs-y += lm63
subdirs-y += rtd2132
+subdirs-y += tpm
subdirs-y += w83795
subdirs-y += w83793
subdirs-y += at24rf08c
diff --git a/src/drivers/i2c/tpm/Kconfig b/src/drivers/i2c/tpm/Kconfig
new file mode 100644
index 0000000..2456204
--- /dev/null
+++ b/src/drivers/i2c/tpm/Kconfig
@@ -0,0 +1,9 @@
+config I2C_TPM
+ bool "I2C TPM"
+ depends on !PC80 # for now
+
+config DRIVER_TPM_I2C_ADDR
+ hex "I2C TPM chip address"
+ default 2 # FIXME, workaround for Kconfig BS
+ depends on I2C_TPM
+
diff --git a/src/drivers/i2c/tpm/Makefile.inc b/src/drivers/i2c/tpm/Makefile.inc
new file mode 100644
index 0000000..74d9b7e
--- /dev/null
+++ b/src/drivers/i2c/tpm/Makefile.inc
@@ -0,0 +1,2 @@
+ramstage-$(CONFIG_I2C_TPM) += tis_i2c.c tpm.c tpm_tis_i2c.c
+romstage-$(CONFIG_I2C_TPM) += tis_i2c.c tpm.c tpm_tis_i2c.c
diff --git a/src/drivers/i2c/tpm/tis.h b/src/drivers/i2c/tpm/tis.h
new file mode 100644
index 0000000..348f9a8
--- /dev/null
+++ b/src/drivers/i2c/tpm/tis.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DRIVERS_TPM_TPM_H__
+#define __DRIVERS_TPM_TPM_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+void tis_set_i2c_bus(unsigned _bus);
+
+/*
+ * tis_init()
+ *
+ * Initialize the TPM device. Returns 0 on success or -1 on
+ * failure (in case device probing did not succeed).
+ */
+int tis_init(void);
+
+/*
+ * 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, -1 on failure.
+ */
+int tis_open(void);
+
+/*
+ * tis_close()
+ *
+ * terminate the currect session with the TPM by releasing the locked
+ * locality. Returns 0 on success of -1 on failure (in case lock
+ * removal did not succeed).
+ */
+int tis_close(void);
+
+/*
+ * 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 -1 on failure.
+ */
+int tis_sendrecv(const uint8_t *sendbuf, size_t send_size, uint8_t *recvbuf,
+ size_t *recv_len);
+
+#endif /* __DRIVERS_TPM_TPM_H__ */
diff --git a/src/drivers/i2c/tpm/tis_i2c.c b/src/drivers/i2c/tpm/tis_i2c.c
new file mode 100644
index 0000000..59971a2
--- /dev/null
+++ b/src/drivers/i2c/tpm/tis_i2c.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <device/i2c.h>
+#include "tis.h"
+#include "tpm.h"
+
+static unsigned bus = 0;
+
+static int opened;
+
+void tis_set_i2c_bus(unsigned _bus)
+{
+ bus = _bus;
+}
+
+int tis_open(void)
+{
+ opened = 1;
+ if (tpm_open(bus, CONFIG_DRIVER_TPM_I2C_ADDR)) {
+ opened = 0;
+ return -1;
+ }
+ return 0;
+}
+
+int tis_close(void)
+{
+ opened = 0;
+ tpm_close();
+ return 0;
+}
+
+int tis_init(void)
+{
+ int chip = CONFIG_DRIVER_TPM_I2C_ADDR;
+
+ /*
+ * Probe TPM twice; the first probing might fail because TPM is asleep,
+ * and the probing can wake up TPM.
+ */
+ uint8_t tmp;
+ if (!bus)
+ return -1;
+
+ if (i2c_read(bus, chip, 0, 0, &tmp, sizeof(tmp)) &&
+ i2c_read(bus, chip, 0, 0, &tmp, sizeof(tmp)))
+ return -1;
+
+ return 0;
+}
+
+int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
+ uint8_t *recvbuf, size_t *rbuf_len)
+{
+ uint8_t buf[TPM_BUFSIZE];
+
+ if (!opened && tis_open())
+ return -1;
+
+ if (sizeof(buf) < sbuf_size)
+ return -1;
+
+ memcpy(buf, sendbuf, sbuf_size);
+
+ int len = tpm_transmit(buf, sbuf_size);
+
+ if (len < 10) {
+ *rbuf_len = 0;
+ return -1;
+ }
+
+ if (len > *rbuf_len) {
+ *rbuf_len = len;
+ return -1;
+ }
+
+ memcpy(recvbuf, buf, len);
+ *rbuf_len = len;
+
+ return 0;
+}
diff --git a/src/drivers/i2c/tpm/tpm.c b/src/drivers/i2c/tpm/tpm.c
new file mode 100644
index 0000000..d31cc79
--- /dev/null
+++ b/src/drivers/i2c/tpm/tpm.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external(a)infineon.com>
+ *
+ * Description:
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * It is based on the Linux kernel driver tpm.c from Leendert van
+ * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
+ *
+ * Version: 2.1.1
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <delay.h>
+#include <console/console.h>
+#include <arch/byteorder.h>
+#include "tis.h"
+#include "tpm.h"
+
+/* global structure for tpm chip data */
+struct tpm_chip g_chip;
+
+#define TPM_CMD_COUNT_BYTE 2
+#define TPM_CMD_ORDINAL_BYTE 6
+
+ssize_t tpm_transmit(const uint8_t *buf, size_t bufsiz)
+{
+ ssize_t rc;
+ uint32_t count, ordinal;
+
+ struct tpm_chip *chip = &g_chip;
+
+ memcpy(&count, buf + TPM_CMD_COUNT_BYTE, sizeof(count));
+ count = be32_to_cpu(count);
+ memcpy(&ordinal, buf + TPM_CMD_ORDINAL_BYTE, sizeof(ordinal));
+ ordinal = be32_to_cpu(ordinal);
+
+ if (count == 0) {
+ printk(BIOS_DEBUG, "tpm_transmit: no data\n");
+ return -1; //ENODATA;
+ }
+ if (count > bufsiz) {
+ printk(BIOS_DEBUG, "tpm_transmit: invalid count value %x %zx\n",
+ count, bufsiz);
+ return -1; //E2BIG;
+ }
+
+ ASSERT(chip->vendor.send);
+ rc = chip->vendor.send(chip, (uint8_t *) buf, count);
+ if (rc < 0) {
+ printk(BIOS_DEBUG, "tpm_transmit: tpm_send: error %zd\n", rc);
+ goto out;
+ }
+
+ if (chip->vendor.irq)
+ goto out_recv;
+
+ int timeout = 2 * 60 * 1000; /* two minutes timeout */
+ while (timeout) {
+ ASSERT(chip->vendor.status);
+ uint8_t status = chip->vendor.status(chip);
+ if ((status & chip->vendor.req_complete_mask) ==
+ chip->vendor.req_complete_val) {
+ goto out_recv;
+ }
+
+ if ((status == chip->vendor.req_canceled)) {
+ printk(BIOS_DEBUG, "tpm_transmit: Operation Canceled\n");
+ rc = -1;
+ goto out;
+ }
+ mdelay(TPM_TIMEOUT);
+ timeout--;
+ }
+
+ ASSERT(chip->vendor.cancel);
+ chip->vendor.cancel(chip);
+ printk(BIOS_DEBUG, "tpm_transmit: Operation Timed out\n");
+ rc = -1; //ETIME;
+ goto out;
+
+out_recv:
+
+ rc = chip->vendor.recv(chip, (uint8_t *) buf, TPM_BUFSIZE);
+ if (rc < 0)
+ printk(BIOS_DEBUG, "tpm_transmit: tpm_recv: error %zd\n", rc);
+out:
+ return rc;
+}
+
+#define TPM_ERROR_SIZE 10
+
+struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry)
+{
+ struct tpm_chip *chip;
+
+ /* Driver specific per-device data */
+ chip = &g_chip;
+ memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific));
+ chip->is_open = 1;
+
+ return chip;
+}
+
+int tpm_open(unsigned bus, uint32_t dev_addr)
+{
+ int rc;
+ if (g_chip.is_open)
+ return -1; //EBUSY;
+ rc = tpm_vendor_init(bus, dev_addr);
+ if (rc < 0)
+ g_chip.is_open = 0;
+ return rc;
+}
+
+void tpm_close(void)
+{
+ if (g_chip.is_open) {
+ tpm_vendor_cleanup(&g_chip);
+ g_chip.is_open = 0;
+ }
+}
diff --git a/src/drivers/i2c/tpm/tpm.h b/src/drivers/i2c/tpm/tpm.h
new file mode 100644
index 0000000..daddc59
--- /dev/null
+++ b/src/drivers/i2c/tpm/tpm.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external(a)infineon.com>
+ *
+ * Version: 2.1.1
+ *
+ * Description:
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * It is based on the Linux kernel driver tpm.c from Leendert van
+ * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
+ *
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DRIVERS_TPM_SLB9635_I2C_TPM_H__
+#define __DRIVERS_TPM_SLB9635_I2C_TPM_H__
+
+#include <stdint.h>
+
+enum tpm_timeout {
+ TPM_TIMEOUT = 1, /* msecs */
+};
+
+/* Size of external transmit buffer (used in tpm_transmit)*/
+#define TPM_BUFSIZE 4096
+
+/* Index of fields in TPM command buffer */
+#define TPM_CMD_SIZE_BYTE 2
+#define TPM_CMD_ORDINAL_BYTE 6
+
+/* Index of Count field in TPM response buffer */
+#define TPM_RSP_SIZE_BYTE 2
+#define TPM_RSP_RC_BYTE 6
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+ const uint8_t req_complete_mask;
+ const uint8_t req_complete_val;
+ const uint8_t req_canceled;
+ int irq;
+ int (*recv)(struct tpm_chip *, uint8_t *, size_t);
+ int (*send)(struct tpm_chip *, uint8_t *, size_t);
+ void (*cancel)(struct tpm_chip *);
+ uint8_t(*status)(struct tpm_chip *);
+ int locality;
+};
+
+struct tpm_chip {
+ int is_open;
+ struct tpm_vendor_specific vendor;
+};
+
+struct tpm_input_header {
+ uint16_t tag;
+ uint32_t length;
+ uint32_t ordinal;
+} __attribute__ ((packed));
+
+struct tpm_output_header {
+ uint16_t tag;
+ uint32_t length;
+ uint32_t return_code;
+} __attribute__ ((packed));
+
+struct timeout_t {
+ uint32_t a;
+ uint32_t b;
+ uint32_t c;
+ uint32_t d;
+} __attribute__ ((packed));
+
+struct duration_t {
+ uint32_t tpm_short;
+ uint32_t tpm_medium;
+ uint32_t tpm_long;
+} __attribute__ ((packed));
+
+typedef union {
+ struct timeout_t timeout;
+ struct duration_t duration;
+} cap_t;
+
+struct tpm_getcap_params_in {
+ uint32_t cap;
+ uint32_t subcap_size;
+ uint32_t subcap;
+} __attribute__ ((packed));
+
+struct tpm_getcap_params_out {
+ uint32_t cap_size;
+ cap_t cap;
+} __attribute__ ((packed));
+
+typedef union {
+ struct tpm_input_header in;
+ struct tpm_output_header out;
+} tpm_cmd_header;
+
+typedef union {
+ struct tpm_getcap_params_out getcap_out;
+ struct tpm_getcap_params_in getcap_in;
+} tpm_cmd_params;
+
+struct tpm_cmd_t {
+ tpm_cmd_header header;
+ tpm_cmd_params params;
+} __attribute__ ((packed));
+
+
+/* ---------- Interface for TPM vendor ------------ */
+
+struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *);
+
+int tpm_vendor_init(unsigned bus, uint32_t dev_addr);
+
+void tpm_vendor_cleanup(struct tpm_chip *chip);
+
+/* ---------- Interface for TDDL ------------------- */
+
+/*
+ * if dev_addr != 0 - redefines TPM device address
+ * Returns < 0 on error, 0 on success.
+ */
+int tpm_open(unsigned bus, uint32_t dev_addr);
+
+void tpm_close(void);
+
+/*
+ * Transmit bufsiz bytes out of buf to TPM and get results back in buf, too.
+ * Returns < 0 on error, 0 on success.
+ */
+ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz);
+
+#endif /* __DRIVERS_TPM_SLB9635_I2C_TPM_H__ */
diff --git a/src/drivers/i2c/tpm/tpm_tis_i2c.c b/src/drivers/i2c/tpm/tpm_tis_i2c.c
new file mode 100644
index 0000000..008fac6
--- /dev/null
+++ b/src/drivers/i2c/tpm/tpm_tis_i2c.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external(a)infineon.com>
+ *
+ * Description:
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0 and the
+ * Infineon I2C Protocol Stack Specification v0.20.
+ *
+ * It is based on the Linux kernel driver tpm.c from Leendert van
+ * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
+ *
+ * Version: 2.1.1
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <types.h>
+#include <delay.h>
+#include <console/console.h>
+#include <arch/byteorder.h>
+#include <device/i2c.h>
+#include "tpm.h"
+
+/* max. buffer size supported by our tpm */
+#ifdef TPM_BUFSIZE
+#undef TPM_BUFSIZE
+#endif
+#define TPM_BUFSIZE 1260
+/* Address of the TPM on the I2C bus */
+#define TPM_I2C_ADDR 0x20
+/* max. number of iterations after i2c NAK */
+#define MAX_COUNT 3
+
+#define SLEEP_DURATION 60 /*in usec*/
+
+/* max. number of iterations after i2c NAK for 'long' commands
+ * we need this especially for sending TPM_READY, since the cleanup after the
+ * transtion to the ready state may take some time, but it is unpredictable
+ * how long it will take.
+ */
+#define MAX_COUNT_LONG 50
+
+#define SLEEP_DURATION_LONG 210 /* in usec */
+
+/* expected value for DIDVID register */
+#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
+#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
+
+enum i2c_chip_type {
+ SLB9635,
+ SLB9645,
+ UNKNOWN,
+};
+
+static const char * const chip_name[] = {
+ [SLB9635] = "slb9635tt",
+ [SLB9645] = "slb9645tt",
+ [UNKNOWN] = "unknown/fallback to slb9635",
+};
+
+/* Structure to store I2C TPM specific stuff */
+struct tpm_inf_dev {
+ unsigned bus;
+ unsigned int addr;
+ uint8_t buf[TPM_BUFSIZE + sizeof(uint8_t)]; // max. buffer size + addr
+ enum i2c_chip_type chip_type;
+};
+
+static struct tpm_inf_dev tpm_dev = {
+ .addr = TPM_I2C_ADDR
+};
+
+/*
+ * iic_tpm_read() - read from TPM register
+ * @addr: register address to read from
+ * @buffer: provided by caller
+ * @len: number of bytes to read
+ *
+ * Read len bytes from TPM register and put them into
+ * buffer (little-endian format, i.e. first byte is put into buffer[0]).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * Return -EIO on error, 0 on success.
+ */
+static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len)
+{
+ int rc;
+ int count;
+
+ if (!tpm_dev.bus)
+ return -1;
+ if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) {
+ /* slb9635 protocol should work in both cases */
+ for (count = 0; count < MAX_COUNT; count++) {
+ rc = i2c_write(tpm_dev.bus, tpm_dev.addr,
+ 0, 0, &addr, 1);
+ if (rc == 0)
+ break; /* success, break to skip sleep */
+
+ udelay(SLEEP_DURATION);
+ }
+
+ if (rc)
+ return -rc;
+
+ /* After the TPM has successfully received the register address
+ * it needs some time, thus we're sleeping here again, before
+ * retrieving the data
+ */
+ for (count = 0; count < MAX_COUNT; count++) {
+ udelay(SLEEP_DURATION);
+ rc = i2c_read(tpm_dev.bus, tpm_dev.addr,
+ 0, 0, buffer, len);
+ if (rc == 0)
+ break; /* success, break to skip sleep */
+
+ }
+ } else {
+ /* use a combined read for newer chips
+ * unfortunately the smbus functions are not suitable due to
+ * the 32 byte limit of the smbus.
+ * retries should usually not be needed, but are kept just to
+ * be safe on the safe side.
+ */
+ for (count = 0; count < MAX_COUNT; count++) {
+ rc = i2c_read(tpm_dev.bus, tpm_dev.addr,
+ addr, 1, buffer, len);
+ if (rc == 0)
+ break; /* break here to skip sleep */
+ udelay(SLEEP_DURATION);
+ }
+ }
+
+ /* take care of 'guard time' */
+ udelay(SLEEP_DURATION);
+ if (rc)
+ return -rc;
+
+ return 0;
+}
+
+static int iic_tpm_write_generic(uint8_t addr, uint8_t *buffer, size_t len,
+ unsigned int sleep_time,
+ uint8_t max_count)
+{
+ int rc = 0;
+ int count;
+
+ if (len > TPM_BUFSIZE) {
+ printk(BIOS_DEBUG, "%s: Length %d is too large\n", __func__, len);
+ return -1;
+ }
+
+ /* prepare send buffer */
+ tpm_dev.buf[0] = addr;
+ memcpy(&(tpm_dev.buf[1]), buffer, len);
+
+ if (!tpm_dev.bus)
+ return -1;
+ for (count = 0; count < max_count; count++) {
+ rc = i2c_write(tpm_dev.bus, tpm_dev.addr, 0, 0,
+ tpm_dev.buf, len + 1);
+ if (rc == 0)
+ break; /* success, break to skip sleep */
+
+ udelay(sleep_time);
+ }
+
+ /* take care of 'guard time' */
+ udelay(SLEEP_DURATION);
+ if (rc)
+ return -rc;
+
+ return 0;
+}
+
+/*
+ * iic_tpm_write() - write to TPM register
+ * @addr: register address to write to
+ * @buffer: containing data to be written
+ * @len: number of bytes to write
+ *
+ * Write len bytes from provided buffer to TPM register (little
+ * endian format, i.e. buffer[0] is written as first byte).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * NOTE: use this function instead of the iic_tpm_write_generic function.
+ *
+ * Return -EIO on error, 0 on success
+ */
+static int iic_tpm_write(uint8_t addr, uint8_t *buffer, size_t len)
+{
+ return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION,
+ MAX_COUNT);
+}
+
+/*
+ * This function is needed especially for the cleanup situation after
+ * sending TPM_READY
+ * */
+static int iic_tpm_write_long(uint8_t addr, uint8_t *buffer, size_t len)
+{
+ return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG,
+ MAX_COUNT_LONG);
+}
+
+#define TPM_HEADER_SIZE 10
+
+enum tis_access {
+ TPM_ACCESS_VALID = 0x80,
+ TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+ TPM_ACCESS_REQUEST_PENDING = 0x04,
+ TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+ TPM_STS_VALID = 0x80,
+ TPM_STS_COMMAND_READY = 0x40,
+ TPM_STS_GO = 0x20,
+ TPM_STS_DATA_AVAIL = 0x10,
+ TPM_STS_DATA_EXPECT = 0x08,
+};
+
+#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
+#define TPM_STS(l) (0x0001 | ((l) << 4))
+#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
+#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
+
+static int check_locality(struct tpm_chip *chip, int loc)
+{
+ uint8_t buf;
+ int rc;
+
+ rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
+ if (rc < 0)
+ return rc;
+
+ if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
+ chip->vendor.locality = loc;
+ return loc;
+ }
+
+ return -1;
+}
+
+static void release_locality(struct tpm_chip *chip, int loc, int force)
+{
+ uint8_t buf;
+ if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
+ return;
+
+ if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
+ buf = TPM_ACCESS_ACTIVE_LOCALITY;
+ iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+ }
+}
+
+static int request_locality(struct tpm_chip *chip, int loc)
+{
+ uint8_t buf = TPM_ACCESS_REQUEST_USE;
+
+ if (check_locality(chip, loc) >= 0)
+ return loc; /* we already have the locality */
+
+ iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+
+ /* wait for burstcount */
+ int timeout = 2 * 1000; /* 2s timeout */
+ while (timeout) {
+ if (check_locality(chip, loc) >= 0)
+ return loc;
+ mdelay(TPM_TIMEOUT);
+ timeout--;
+ }
+
+ return -1;
+}
+
+static uint8_t tpm_tis_i2c_status(struct tpm_chip *chip)
+{
+ /* NOTE: since i2c read may fail, return 0 in this case --> time-out */
+ uint8_t buf;
+ if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+ return 0;
+ else
+ return buf;
+}
+
+static void tpm_tis_i2c_ready(struct tpm_chip *chip)
+{
+ /* this causes the current command to be aborted */
+ uint8_t buf = TPM_STS_COMMAND_READY;
+ iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+}
+
+static ssize_t get_burstcount(struct tpm_chip *chip)
+{
+ ssize_t burstcnt;
+ uint8_t buf[3];
+
+ /* wait for burstcount */
+ int timeout = 2 * 1000; /* 2s timeout */
+ while (timeout) {
+ /* Note: STS is little endian */
+ if (iic_tpm_read(TPM_STS(chip->vendor.locality) + 1, buf, 3) < 0)
+ burstcnt = 0;
+ else
+ burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+ if (burstcnt)
+ return burstcnt;
+ mdelay(TPM_TIMEOUT);
+ timeout--;
+ }
+ return -1; //EBUSY;
+}
+
+static int wait_for_stat(struct tpm_chip *chip, uint8_t mask, int *status)
+{
+ unsigned long timeout = 2 * 1024;
+ while (timeout) {
+ *status = tpm_tis_i2c_status(chip);
+ if ((*status & mask) == mask)
+ return 0;
+ mdelay(TPM_TIMEOUT);
+ timeout--;
+ }
+
+ return -1; //ETIME;
+}
+
+static int recv_data(struct tpm_chip *chip, uint8_t *buf, size_t count)
+{
+ size_t size = 0;
+ ssize_t burstcnt;
+ int rc;
+
+ while (size < count) {
+ burstcnt = get_burstcount(chip);
+
+ /* burstcount < 0 = tpm is busy */
+ if (burstcnt < 0)
+ return burstcnt;
+
+ /* limit received data to max. left */
+ if (burstcnt > (count - size))
+ burstcnt = count - size;
+
+ rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
+ &(buf[size]),
+ burstcnt);
+ if (rc == 0)
+ size += burstcnt;
+
+ }
+ return size;
+}
+
+static int tpm_tis_i2c_recv(struct tpm_chip *chip, uint8_t *buf, size_t count)
+{
+ int size = 0;
+ uint32_t expected;
+ int status;
+
+ if (count < TPM_HEADER_SIZE) {
+ size = -1; //EIO;
+ goto out;
+ }
+
+ /* read first 10 bytes, including tag, paramsize, and result */
+ size = recv_data(chip, buf, TPM_HEADER_SIZE);
+ if (size < TPM_HEADER_SIZE) {
+ printk(BIOS_DEBUG, "tpm_tis_i2c_recv: Unable to read header\n");
+ goto out;
+ }
+
+ memcpy(&expected, buf + TPM_RSP_SIZE_BYTE, sizeof(expected));
+ expected = be32_to_cpu(expected);
+ if ((size_t)expected > count) {
+ size = -1; //EIO;
+ goto out;
+ }
+
+ size += recv_data(chip, &buf[TPM_HEADER_SIZE],
+ expected - TPM_HEADER_SIZE);
+ if (size < expected) {
+ printk(BIOS_DEBUG, "tpm_tis_i2c_recv: Unable to "
+ "read remainder of result\n");
+ size = -1; //ETIME;
+ goto out;
+ }
+
+ wait_for_stat(chip, TPM_STS_VALID, &status);
+ if (status & TPM_STS_DATA_AVAIL) { /* retry? */
+ printk(BIOS_DEBUG, "tpm_tis_i2c_recv: Error left over data\n");
+ size = -1; //EIO;
+ goto out;
+ }
+
+out:
+ tpm_tis_i2c_ready(chip);
+
+ return size;
+}
+
+static int tpm_tis_i2c_send(struct tpm_chip *chip, uint8_t *buf, size_t len)
+{
+ int rc, status;
+ ssize_t burstcnt;
+ size_t count = 0;
+ uint8_t sts = TPM_STS_GO;
+
+ if (len > TPM_BUFSIZE)
+ return -1; //E2BIG; /* command is too long for our tpm, sorry */
+
+ status = tpm_tis_i2c_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ tpm_tis_i2c_ready(chip);
+ if (wait_for_stat(chip, TPM_STS_COMMAND_READY, &status) < 0) {
+ rc = -1; //ETIME;
+ goto out_err;
+ }
+ }
+
+ while (count < len - 1) {
+ burstcnt = get_burstcount(chip);
+
+ /* burstcount < 0 = tpm is busy */
+ if (burstcnt < 0)
+ return burstcnt;
+
+ if (burstcnt > (len-1-count))
+ burstcnt = len-1-count;
+
+#ifdef CONFIG_TPM_I2C_BURST_LIMITATION
+ if (burstcnt > CONFIG_TPM_I2C_BURST_LIMITATION)
+ burstcnt = CONFIG_TPM_I2C_BURST_LIMITATION;
+#endif /* CONFIG_TPM_I2C_BURST_LIMITATION */
+
+ rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
+ &(buf[count]), burstcnt);
+ if (rc == 0)
+ count += burstcnt;
+
+ wait_for_stat(chip, TPM_STS_VALID, &status);
+
+ if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ rc = -1; //EIO;
+ goto out_err;
+ }
+
+ }
+
+ /* write last byte */
+ iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
+ wait_for_stat(chip, TPM_STS_VALID, &status);
+ if ((status & TPM_STS_DATA_EXPECT) != 0) {
+ rc = -1; //EIO;
+ goto out_err;
+ }
+
+ /* go and do it */
+ iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+
+ return len;
+out_err:
+ tpm_tis_i2c_ready(chip);
+
+ return rc;
+}
+
+static struct tpm_vendor_specific tpm_tis_i2c = {
+ .status = tpm_tis_i2c_status,
+ .recv = tpm_tis_i2c_recv,
+ .send = tpm_tis_i2c_send,
+ .cancel = tpm_tis_i2c_ready,
+ .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ .req_canceled = TPM_STS_COMMAND_READY,
+};
+
+/* initialisation of i2c tpm */
+
+
+int tpm_vendor_init(unsigned bus, uint32_t dev_addr)
+{
+ uint32_t vendor;
+ unsigned int old_addr;
+ int rc = 0;
+ struct tpm_chip *chip;
+
+ old_addr = tpm_dev.addr;
+ if (dev_addr != 0)
+ tpm_dev.addr = dev_addr;
+ tpm_dev.bus = bus;
+
+ chip = tpm_register_hardware(&tpm_tis_i2c);
+ if (chip < 0) {
+ rc = -1; //ENODEV;
+ goto out_err;
+ }
+
+ /* Disable interrupts (not supported) */
+ chip->vendor.irq = 0;
+
+ if (request_locality(chip, 0) != 0) {
+ rc = -1; //ENODEV;
+ goto out_err;
+ }
+
+ /* read four bytes from DID_VID register */
+ if (iic_tpm_read(TPM_DID_VID(0), (uint8_t *)&vendor, 4) < 0) {
+ rc = -1; //EIO;
+ goto out_release;
+ }
+
+ if (vendor == TPM_TIS_I2C_DID_VID_9645) {
+ tpm_dev.chip_type = SLB9645;
+ } else if (be32_to_cpu(vendor) == TPM_TIS_I2C_DID_VID_9635) {
+ tpm_dev.chip_type = SLB9635;
+ } else {
+ printk(BIOS_DEBUG, "Vendor ID 0x%08x not recognized.\n", vendor);
+ rc = -1; //ENODEV;
+ goto out_release;
+ }
+
+ printk(BIOS_DEBUG, "1.2 TPM (chip type %s device-id 0x%X)\n",
+ chip_name[tpm_dev.chip_type], vendor >> 16);
+
+ /*
+ * A timeout query to TPM can be placed here.
+ * Standard timeout values are used so far
+ */
+
+ return 0;
+
+out_release:
+ release_locality(chip, 0, 1);
+
+out_err:
+ tpm_dev.addr = old_addr;
+ return rc;
+}
+
+void tpm_vendor_cleanup(struct tpm_chip *chip)
+{
+ release_locality(chip, chip->vendor.locality, 1);
+}
diff --git a/src/drivers/pc80/Kconfig b/src/drivers/pc80/Kconfig
index 3572bc2..5fad3a5 100644
--- a/src/drivers/pc80/Kconfig
+++ b/src/drivers/pc80/Kconfig
@@ -15,7 +15,7 @@ config DRIVERS_PS2_KEYBOARD
this option, then you can say N here to speed up boot time.
Otherwise say Y.
-config TPM
+config LPC_TPM
bool
default n
help
diff --git a/src/drivers/pc80/Makefile.inc b/src/drivers/pc80/Makefile.inc
index d216e2c..a60033b 100644
--- a/src/drivers/pc80/Makefile.inc
+++ b/src/drivers/pc80/Makefile.inc
@@ -8,7 +8,7 @@ ramstage-y += keyboard.c
ramstage-$(CONFIG_SPKMODEM) += spkmodem.c
romstage-$(CONFIG_USE_OPTION_TABLE) += mc146818rtc_early.c
-romstage-$(CONFIG_TPM) += tpm.c
+romstage-$(CONFIG_LPC_TPM) += tpm.c
romstage-$(CONFIG_SPKMODEM) += spkmodem.c
subdirs-y += vga
the following patch was just integrated into master:
commit ca63027ef7627ba60f6776f2c9bd373cdb2afbb0
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Tue Aug 5 10:48:20 2014 -0500
cbfstool: process cbfs_payload_segment(s) in host byte order
The printing routines of the cbfs_payload_segment assumed the type
could be accessed in host order. Each of the fields need to be
converted to the host order before inspecting the fields. In addition,
this removes all the ntoh*() calls while processing the
cbfs_payload_segment structures.
cbfstool would crash adding entries or just printing entries
containing a payload when -v was passed on the command line.
Change-Id: Iff41c64a99001b9e3920e2e26828c5fd6e671239
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
Reviewed-on: http://review.coreboot.org/6498
Tested-by: build bot (Jenkins)
Reviewed-by: Paul Menzel <paulepanter(a)users.sourceforge.net>
Reviewed-by: Martin Roth <gaumless(a)gmail.com>
See http://review.coreboot.org/6498 for details.
-gerrit
Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6518
-gerrit
commit 98d4431360763e3c93460a3d025fea83a0c013bb
Author: Duncan Laurie <dlaurie(a)chromium.org>
Date: Wed Aug 28 09:53:57 2013 -0700
falco: Add double function reset to ALC283 verb table
The ALC283 needs a double function reset to ensure that all settings
are reset and the firmware beep is functional.
Original-Change-Id: Id9ddc6f4914957f39c5f9cdfaaac354808929146
Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167291
Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
Commit-Queue: Duncan Laurie <dlaurie(a)google.com>
(cherry picked from commit c59865ac464af308baedcd69aa662f46ff3a04d3)
Change-Id: Ie6f3a8179376bc97a6d22712dd965f5e0e6ec5d6
Signed-off-by: Duncan Laurie <dlaurie(a)chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167313
Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
(cherry picked from commit b31d7a31b838e67a4b7f33119a3baea049d30a36)
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
src/mainboard/google/falco/hda_verb.h | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/mainboard/google/falco/hda_verb.h b/src/mainboard/google/falco/hda_verb.h
index ba0da1c..ae7dbb3 100644
--- a/src/mainboard/google/falco/hda_verb.h
+++ b/src/mainboard/google/falco/hda_verb.h
@@ -21,7 +21,12 @@ static const u32 mainboard_cim_verb_data[] = {
/* coreboot specific header */
0x10ec0283, // Codec Vendor / Device ID: Realtek ALC283
0x10ec0283, // Subsystem ID
- 0x0000000c, // Number of jacks (NID entries)
+ 0x0000000d, // Number of jacks (NID entries)
+
+ 0x0017ff00, // Function Reset
+ 0x0017ff00, // Double Function Reset
+ 0x000F0000, // Pad - get vendor id
+ 0x000F0002, // Pad - get revision id
/* NID 0x01, HDA Codec Subsystem ID Verb Table: 0x10ec0283 */
0x00172083,
Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6517
-gerrit
commit d791a9ecdd17320c53d154b8e5eaa7ceada7807a
Author: Ronald G. Minnich <rminnich(a)gmail.com>
Date: Fri Aug 23 11:22:42 2013 -0700
ARMV7: threading support for cooperative multitasking
These functions add support for cooperative multitasking.
Currently, since we only have one ARM SOC that uses or supports multitasking,
arch_get_thread_stackbase returns CONFIG_STACK_BOTTOM for the thread stack.
We may end up having to make a cpu-specific function that arch_get_thread_stackbase calls,
but let's avoid adding complexity until we're sure we need to. We also wish to avoid
creating Yet Another Config Variable but will do so if pressed.
The switch code only saves r4-r11 and lr, which is consistent with the standard.
Change-Id: I0338a9c11127351e1f3a190bc51a7a558420b141
Signed-off-by: Ronald G. Minnich <rminnich(a)gmail.com>
Reviewed-on: https://gerrit.chromium.org/gerrit/66845
Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
Commit-Queue: Ronald G. Minnich <rminnich(a)chromium.org>
Tested-by: Ronald G. Minnich <rminnich(a)chromium.org>
(cherry picked from commit 22b62af3c26b6b504498b434d29a56a8932f3061)
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
src/arch/armv7/thread.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 127 insertions(+)
diff --git a/src/arch/armv7/thread.c b/src/arch/armv7/thread.c
new file mode 100644
index 0000000..d0c23ff
--- /dev/null
+++ b/src/arch/armv7/thread.c
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 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
+ */
+#include <console/console.h>
+#include <thread.h>
+
+/* The stack frame looks like the following. */
+struct pushed_regs {
+ u32 r4;
+ u32 r5;
+ u32 r6;
+ u32 r7;
+ u32 r8;
+ u32 r9;
+ u32 r10;
+ u32 r11;
+ u32 lr;
+};
+
+static inline uintptr_t push_stack(uintptr_t cur_stack, uintptr_t value)
+{
+ uintptr_t *addr;
+
+ cur_stack -= sizeof(value);
+ addr = (uintptr_t *)cur_stack;
+ *addr = value;
+ return cur_stack;
+}
+
+void arch_prepare_thread(struct thread *t,
+ void asmlinkage (*thread_entry)(void *), void *arg)
+{
+ uintptr_t stack = t->stack_current;
+ int i;
+ uintptr_t poison = 0xdeadbeef;
+
+ /* Push the LR. thread_entry()
+ * is assumed to never return.
+ */
+ stack = push_stack(stack, (uintptr_t)thread_entry);
+ /* Make room for the registers.
+ * Poison the initial stack. This is good hygiene and finds bugs.
+ * Poisoning the stack with different values helps when you're
+ * hunting for (e.g.) misaligned stacks or other such
+ * weirdness. The -1 is because we already pushed lr.
+ */
+ for(i = 0; i < sizeof(struct pushed_regs)/sizeof(u32)-1; i++)
+ stack = push_stack(stack, poison++);
+
+ t->stack_current = stack;
+}
+
+/* We could write this as a .S and the first time around that's how we
+ * did it. But there's always the question of matching our ARM
+ * directives in the .S with how gcc is doing things. It seems best
+ * to follow the pattern of the rest of the ARM port and just use
+ * inline assembly and let gcc get all the ELF magic right.
+ */
+void __attribute__((naked))
+switch_to_thread(uintptr_t new_stack, uintptr_t *saved_stack)
+{
+ /* Defintions for those of us not totally familiar with ARM:
+ * R15 -- PC, R14 -- LR, R13 -- SP
+ * R0-R3 need not be saved, nor R12.
+ * on entry, the only saved state is in LR -- the old PC.
+ * The args are in R0,R1.
+ * R0 is the new stack
+ * R1 is a pointer to the old stack save location
+ * Push R4-R11 and LR
+ * then switch stacks
+ * then pop R0-R12 and LR
+ * then mov PC,LR
+ *
+ * stack layout
+ * +------------+
+ * | LR | <-- sp + 0x20
+ * +------------+
+ * | R11 | <-- sp + 0x1c
+ * +------------+
+ * | R10 | <-- sp + 0x18
+ * +------------+
+ * | R9 | <-- sp + 0x14
+ * +------------+
+ * | R8 | <-- sp + 0x10
+ * +------------+
+ * | R7 | <-- sp + 0x0c
+ * +------------+
+ * | R6 | <-- sp + 0x08
+ * +------------+
+ * | R5 | <-- sp + 0x04
+ * +------------+
+ * | R4 | <-- sp + 0x00
+ * +------------+
+ */
+ asm volatile (
+ /* save context. */
+ "push {r4-r11,lr}\n\t"
+ /* Save the current stack */
+ "str sp,[r1]\n\t"
+ /* switch to the new stack */
+ "mov sp,r0\n\t"
+ /* restore the registers */
+ "pop {r4-r11,lr}\n\t"
+ /* resume other thread. */
+ "mov pc,lr\n\t"
+ );
+}
+
+void *arch_get_thread_stackbase(void)
+{
+ return (void *)CONFIG_STACK_BOTTOM;
+}
Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6516
-gerrit
commit cd2bc1d33c43d26bc011f7d883bb6fa67276f266
Author: Julius Werner <jwerner(a)chromium.org>
Date: Fri Sep 27 12:45:11 2013 -0700
libpayload: xhci: Use Event Data TRBs for transfer event generation
The current XHCI code only sets IOC on the last TRB of a TD, and
doesn't set ISP anywhere. On my Synopsys DesignWare3 controller, this
won't generate an event at all when we have a short transfer that is not
on the last TRB of a TD, resulting in event ring desync and everyone
having a bad time. However, just setting ISP on other TRBs doesn't
really make for a nice solution: we then need to do ugly special casing
to fish out the spurious second transfer event you get for short
packets, and we still need a way to figure out how many bytes were
transferred. Since the Short Packet transfer event only reports
untransferred bytes for the current TRB, we would have to manually walk
the rest of the unprocessed TRB chain and add up the bytes. Check out
U-Boot and the Linux kernel to see how complicated this looks in
practice.
Now what if we had a way to just tell the HC "I want an event at exactly
*this* point in the TD, I want it to have the right completion code for
the whole TD, and to contain the exact number of bytes written"? Enter
the Event Data TRB: this little gizmo really does pretty much exactly
what any sane XHCI driver would want, and I have no idea why it isn't
used more often. It solves both the short packet event generation and
counting the transferred bytes without requiring any special magic in
software.
Change-Id: Idab412d61edf30655ec69c80066bfffd80290403
Signed-off-by: Julius Werner <jwerner(a)chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/170980
Reviewed-by: Stefan Reinauer <reinauer(a)google.com>
Reviewed-by: Kees Cook <keescook(a)chromium.org>
(cherry picked from commit e512c8bcaa5b8e05cae3b9d04cd4947298de999d)
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
payloads/libpayload/drivers/usb/xhci.c | 19 +++++++++++--------
payloads/libpayload/drivers/usb/xhci_private.h | 3 ++-
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c
index 2e9dd3b..d31a8e2 100644
--- a/payloads/libpayload/drivers/usb/xhci.c
+++ b/payloads/libpayload/drivers/usb/xhci.c
@@ -551,6 +551,7 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps,
trb->ptr_low = virt_to_phys(cur_start);
TRB_SET(TL, trb, cur_length);
TRB_SET(TDS, trb, packets);
+ TRB_SET(CH, trb, 1);
/* Check for first, data stage TRB */
if (!trb_count && ep == 1) {
@@ -560,17 +561,19 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps,
TRB_SET(TT, trb, TRB_NORMAL);
}
- /* Check for last TRB */
- if (!length)
- TRB_SET(IOC, trb, 1);
- else
- TRB_SET(CH, trb, 1);
-
xhci_enqueue_trb(tr);
cur_start += cur_length;
++trb_count;
}
+
+ trb = tr->cur;
+ xhci_clear_trb(trb, tr->pcs);
+ trb->ptr_low = virt_to_phys(trb); /* for easier debugging only */
+ TRB_SET(TT, trb, TRB_EVENT_DATA);
+ TRB_SET(IOC, trb, 1);
+
+ xhci_enqueue_trb(tr);
}
static int
@@ -583,7 +586,7 @@ xhci_control(usbdev_t *const dev, const direction_t dir,
transfer_ring_t *const tr = di->transfer_rings[1];
const size_t off = (size_t)data & 0xffff;
- if ((off + dalen) > ((TRANSFER_RING_SIZE - 3) << 16)) {
+ if ((off + dalen) > ((TRANSFER_RING_SIZE - 4) << 16)) {
xhci_debug("Unsupported transfer size\n");
return 1;
}
@@ -670,7 +673,7 @@ xhci_bulk(endpoint_t *const ep,
transfer_ring_t *const tr = di->transfer_rings[ep_id];
const size_t off = (size_t)data & 0xffff;
- if ((off + size) > ((TRANSFER_RING_SIZE - 1) << 16)) {
+ if ((off + size) > ((TRANSFER_RING_SIZE - 2) << 16)) {
xhci_debug("Unsupported transfer size\n");
return 1;
}
diff --git a/payloads/libpayload/drivers/usb/xhci_private.h b/payloads/libpayload/drivers/usb/xhci_private.h
index 945c7f1..c7048f4 100644
--- a/payloads/libpayload/drivers/usb/xhci_private.h
+++ b/payloads/libpayload/drivers/usb/xhci_private.h
@@ -65,7 +65,7 @@ enum { XHCI_FULL_SPEED = 1, XHCI_LOW_SPEED = 2, XHCI_HIGH_SPEED = 3, XHCI_SUPER_
enum {
TRB_NORMAL = 1,
TRB_SETUP_STAGE = 2, TRB_DATA_STAGE = 3, TRB_STATUS_STAGE = 4,
- TRB_LINK = 6,
+ TRB_LINK = 6, TRB_EVENT_DATA = 7,
TRB_CMD_ENABLE_SLOT = 9, TRB_CMD_DISABLE_SLOT = 10, TRB_CMD_ADDRESS_DEV = 11,
TRB_CMD_CONFIGURE_EP = 12, TRB_CMD_EVAL_CTX = 13, TRB_CMD_RESET_EP = 14,
TRB_CMD_STOP_EP = 15, TRB_CMD_SET_TR_DQ = 16, TRB_CMD_NOOP = 23,
@@ -150,6 +150,7 @@ typedef struct {
u8 adv;
} event_ring_t;
+/* Never raise this above 256 to prevent transfer event length overflow! */
#define TRANSFER_RING_SIZE 32
typedef struct {
trb_t *ring;
Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6515
-gerrit
commit 4293a25b1d3ef35c37f5444f559d08457e26c265
Author: David Hendricks <dhendrix(a)chromium.org>
Date: Wed Aug 28 12:39:11 2013 -0700
armv7: Fix dcache writethrough policy handling
The "bufferable" bit was erroneously set for the writethrough policy
making it the same as writeback.
(credit to jwerner for pointing this out)
Signed-off-by: David Hendricks <dhendrix(a)chromium.org>
Change-Id: I567d57f0e522cb4b82988894ba9b4638642bf8db
Reviewed-on: https://chromium-review.googlesource.com/167323
Reviewed-by: Julius Werner <jwerner(a)chromium.org>
Tested-by: David Hendricks <dhendrix(a)chromium.org>
Tested-by: ron minnich <rminnich(a)chromium.org>
Commit-Queue: David Hendricks <dhendrix(a)chromium.org>
(cherry picked from commit 36cf13839604c349692865475f3011afd08965b4)
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
src/arch/armv7/mmu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/arch/armv7/mmu.c b/src/arch/armv7/mmu.c
index 7d6d46a..a350895 100644
--- a/src/arch/armv7/mmu.c
+++ b/src/arch/armv7/mmu.c
@@ -92,7 +92,7 @@ void mmu_config_range(unsigned long start_mb, unsigned long size_mb,
str = "writeback";
break;
case DCACHE_WRITETHROUGH:
- attr = (0x3 << 10) | (1 << 3) | (1 << 2) | 0x2;
+ attr = (0x3 << 10) | (1 << 3) | 0x2;
str = "writethrough";
break;
default:
Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6514
-gerrit
commit 3c94e97abdee585b8b6489baaeb4f172f3ba6b0c
Author: Ronald G. Minnich <rminnich(a)gmail.com>
Date: Tue Aug 27 14:06:19 2013 -0700
Exynos 5420: skip the EDID read if there is already an EDID.
For many boards, the EDID is known and is set in the ramstage. Reading
the EDID is slow and if we have it we do not want to reread it.
If the raw_edid struct member is non-null, skip reading the EDID.
Change-Id: I63fb11aa90b2f739a351cdc3209faac2713ea451
Signed-off-by: Ronald G. Minnich <rminnich(a)gmail.com>
Reviewed-on: https://chromium-review.googlesource.com/167116
Reviewed-by: Gabe Black <gabeblack(a)google.com>
Tested-by: ron minnich <rminnich(a)chromium.org>
Commit-Queue: Ronald Minnich <rminnich(a)google.com>
(cherry picked from commit 80f48655570de544a7e1939c4f5f28713f11d829)
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
src/cpu/samsung/exynos5420/dp.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/cpu/samsung/exynos5420/dp.c b/src/cpu/samsung/exynos5420/dp.c
index 59a4647..b6f83b6 100644
--- a/src/cpu/samsung/exynos5420/dp.c
+++ b/src/cpu/samsung/exynos5420/dp.c
@@ -241,10 +241,15 @@ static unsigned int exynos_dp_handle_edid(struct edp_device_info *edp_info)
return -1;
}
- ret = exynos_dp_read_edid();
- if (ret != EXYNOS_DP_SUCCESS) {
- printk(BIOS_ERR, "DP exynos_dp_read_edid() failed\n");
- return -1;
+ if (edp_info->raw_edid){
+ ret = EXYNOS_DP_SUCCESS;
+ printk(BIOS_SPEW, "EDID compiled in, skipping read\n");
+ } else {
+ ret = exynos_dp_read_edid();
+ if (ret != EXYNOS_DP_SUCCESS) {
+ printk(BIOS_ERR, "DP exynos_dp_read_edid() failed\n");
+ return -1;
+ }
}
return ret;
Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6513
-gerrit
commit e4c06223da87cca067975853209bc2cd4efe22e5
Author: Ronald G. Minnich <rminnich(a)gmail.com>
Date: Wed Aug 21 16:03:32 2013 -0700
Possible thread stack implementation.
Architecture provides a function for thread stack base, thread code uses it.
Build and boot tested on Falco with multitasking on and off.
Change-Id: I5016fab47f9954379acf7702ac7965b0a70c88ed
Signed-off-by: Ronald G. Minnich <rminnich(a)gmail.com>
Reviewed-on: https://gerrit.chromium.org/gerrit/66578
Reviewed-by: Aaron Durbin <adurbin(a)chromium.org>
Commit-Queue: Ronald G. Minnich <rminnich(a)chromium.org>
Tested-by: Ronald G. Minnich <rminnich(a)chromium.org>
(cherry picked from commit 3c6afef30c1a0ad6fba0fb76acc792184d924247)
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
src/arch/x86/lib/thread.c | 7 +++++++
src/include/thread.h | 5 +++++
src/lib/thread.c | 6 ++++--
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/arch/x86/lib/thread.c b/src/arch/x86/lib/thread.c
index 06f8a15..b1549b5 100644
--- a/src/arch/x86/lib/thread.c
+++ b/src/arch/x86/lib/thread.c
@@ -56,3 +56,10 @@ void arch_prepare_thread(struct thread *t,
t->stack_current = stack;
}
+
+void *arch_get_thread_stackbase(void)
+{
+ /* defined in c_start.S */
+ extern u8 thread_stacks[];
+ return &thread_stacks[0];
+}
diff --git a/src/include/thread.h b/src/include/thread.h
index 0522337..306aad6 100644
--- a/src/include/thread.h
+++ b/src/include/thread.h
@@ -38,6 +38,11 @@ struct thread {
};
void threads_initialize(void);
+/* Get the base of the thread stacks.
+ * Returns pointer to CONFIG_NUM_THREADS*CONFIG_STACK_SIZE contiguous bytes
+ * aligned to CONFIG_STACK_SIZE, or NULL.
+ */
+void *arch_get_thread_stackbase(void);
/* Run func(arrg) on a new thread. Return 0 on successful start of thread, < 0
* when thread could not be started. Note that the thread will block the
* current state in the boot state machine until it is complete. */
diff --git a/src/lib/thread.c b/src/lib/thread.c
index 6508bfa..089ae3f 100644
--- a/src/lib/thread.c
+++ b/src/lib/thread.c
@@ -28,7 +28,6 @@ static void idle_thread_init(void);
/* There needs to be at least one thread to run the ramstate state machine. */
#define TOTAL_NUM_THREADS (CONFIG_NUM_THREADS + 1)
-extern char thread_stacks[CONFIG_NUM_THREADS*CONFIG_STACK_SIZE];
/* Storage space for the thread structs .*/
static struct thread all_threads[TOTAL_NUM_THREADS];
@@ -259,8 +258,11 @@ void threads_initialize(void)
{
int i;
struct thread *t;
- char *stack_top;
+ u8 *stack_top;
struct cpu_info *ci;
+ u8 *thread_stacks;
+
+ thread_stacks = arch_get_thread_stackbase();
/* Initialize the BSP thread first. The cpu_info structure is assumed
* to be just under the top of the stack. */
Isaac Christensen (isaac.christensen(a)se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6512
-gerrit
commit 38549d20434692bc75bde7a3b54d5929c87d0080
Author: David Hendricks <dhendrix(a)chromium.org>
Date: Thu Aug 22 16:52:38 2013 -0700
exynos5420: Set the CLK_DIV_CPERI1 value as per manual
Set the CLK_DIV_CPERI1 value as recommended by the
0.02 UM section 7.9.1.25.
This suggests to use 0x3F3F0000 as the value to be
set to save power.
This is ported from https://gerrit.chromium.org/gerrit/#/c/64905
Signed-off-by: David Hendricks <dhendrix(a)chromium.org>
Change-Id: I89a6a72d20374a513019a272628a05e139b31773
Reviewed-on: https://gerrit.chromium.org/gerrit/66787
Commit-Queue: David Hendricks <dhendrix(a)chromium.org>
Tested-by: David Hendricks <dhendrix(a)chromium.org>
Reviewed-by: Ronald G. Minnich <rminnich(a)chromium.org>
(cherry picked from commit 34be13b008e262c641268b7c1c6a08e49f18fc37)
Signed-off-by: Isaac Christensen <isaac.christensen(a)se-eng.com>
---
src/cpu/samsung/exynos5420/clock_init.c | 2 ++
src/cpu/samsung/exynos5420/setup.h | 2 ++
2 files changed, 4 insertions(+)
diff --git a/src/cpu/samsung/exynos5420/clock_init.c b/src/cpu/samsung/exynos5420/clock_init.c
index eeeda90..15cd0c0 100644
--- a/src/cpu/samsung/exynos5420/clock_init.c
+++ b/src/cpu/samsung/exynos5420/clock_init.c
@@ -209,6 +209,8 @@ void system_clock_init(void)
writel(CLK_DIV_PERIC3_VAL, &clk->clk_div_peric3);
writel(CLK_DIV_PERIC4_VAL, &clk->clk_div_peric4);
+ writel(CLK_DIV_CPERI1_VAL, &clk->clk_div_cperi1);
+
writel(CLK_DIV2_RATIO, &clk->clkdiv2_ratio);
writel(CLK_DIV4_RATIO, &clk->clkdiv4_ratio);
writel(CLK_DIV_G2D, &clk->clk_div_g2d);
diff --git a/src/cpu/samsung/exynos5420/setup.h b/src/cpu/samsung/exynos5420/setup.h
index 8f14a91..ca7281d 100644
--- a/src/cpu/samsung/exynos5420/setup.h
+++ b/src/cpu/samsung/exynos5420/setup.h
@@ -48,6 +48,8 @@ struct exynos5_phy_control;
#define APLL_FOUT (1 << 0)
#define KPLL_FOUT (1 << 0)
+#define CLK_DIV_CPERI1_VAL 0x3f3f0000
+
/* APLL_CON1 */
#define APLL_CON1_VAL (0x0020f300)