*
* 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
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
+#include <dm.h>
#include <fdtdec.h>
-#include <compiler.h>
+#include <linux/compiler.h>
#include <i2c.h>
#include <tpm.h>
#include <asm-generic/errno.h>
#include <linux/types.h>
#include <linux/unaligned/be_byteshift.h>
-#include "tpm_private.h"
+#include "tpm_tis_i2c.h"
DECLARE_GLOBAL_DATA_PTR;
-/* Address of the TPM on the I2C bus */
-#define TPM_I2C_ADDR 0x20
-
-/* Max buffer size supported by our tpm */
-#define TPM_DEV_BUFSIZE 1260
-
/* Max number of iterations after i2c NAK */
#define MAX_COUNT 3
#define TPM_HEADER_SIZE 10
-/*
- * Expected value for DIDVID register
- *
- * The only device the system knows about at this moment is Infineon slb9635.
- */
-#define TPM_TIS_I2C_DID_VID 0x000b15d1L
-
enum tis_access {
TPM_ACCESS_VALID = 0x80,
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
#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",
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
-/* Structure to store I2C TPM specific stuff */
-struct tpm_dev {
- uint addr;
- u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */
- enum i2c_chip_type chip_type;
+enum tpm_duration {
+ TPM_SHORT = 0,
+ TPM_MEDIUM = 1,
+ TPM_LONG = 2,
+ TPM_UNDEFINED,
+};
+
+/* Extended error numbers from linux (see errno.h) */
+#define ECANCELED 125 /* Operation Canceled */
+
+/* Timer frequency. Corresponds to msec timer resolution*/
+#define HZ 1000
+
+#define TPM_MAX_ORDINAL 243
+#define TPM_MAX_PROTECTED_ORDINAL 12
+#define TPM_PROTECTED_ORDINAL_MASK 0xFF
+
+#define TPM_CMD_COUNT_BYTE 2
+#define TPM_CMD_ORDINAL_BYTE 6
+
+/*
+ * Array with one entry per ordinal defining the maximum amount
+ * of time the chip could take to return the result. The ordinal
+ * designation of short, medium or long is defined in a table in
+ * TCG Specification TPM Main Part 2 TPM Structures Section 17. The
+ * values of the SHORT, MEDIUM, and LONG durations are retrieved
+ * from the chip during initialization with a call to tpm_get_timeouts.
+ */
+static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = {
+ TPM_UNDEFINED, /* 0 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 5 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 10 */
+ TPM_SHORT,
};
-static struct tpm_dev tpm_dev = {
- .addr = TPM_I2C_ADDR
+static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
+ TPM_UNDEFINED, /* 0 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 5 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 10 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_LONG,
+ TPM_LONG,
+ TPM_MEDIUM, /* 15 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_LONG,
+ TPM_SHORT, /* 20 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_SHORT, /* 25 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM, /* 30 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 35 */
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 40 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 45 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_LONG,
+ TPM_MEDIUM, /* 50 */
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 55 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 60 */
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM, /* 65 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 70 */
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 75 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_LONG, /* 80 */
+ TPM_UNDEFINED,
+ TPM_MEDIUM,
+ TPM_LONG,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 85 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 90 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 95 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 100 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 105 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 110 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 115 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_LONG, /* 120 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_SHORT, /* 125 */
+ TPM_SHORT,
+ TPM_LONG,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT, /* 130 */
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_UNDEFINED, /* 135 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 140 */
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 145 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 150 */
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 155 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 160 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 165 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_LONG, /* 170 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 175 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_MEDIUM, /* 180 */
+ TPM_SHORT,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM, /* 185 */
+ TPM_SHORT,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 190 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 195 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 200 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_SHORT, /* 205 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_MEDIUM, /* 210 */
+ TPM_UNDEFINED,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_MEDIUM,
+ TPM_UNDEFINED, /* 215 */
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT,
+ TPM_SHORT, /* 220 */
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_SHORT,
+ TPM_UNDEFINED, /* 225 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 230 */
+ TPM_LONG,
+ TPM_MEDIUM,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED, /* 235 */
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_UNDEFINED,
+ TPM_SHORT, /* 240 */
+ TPM_UNDEFINED,
+ TPM_MEDIUM,
};
-static struct tpm_dev tpm_dev;
+/* TPM configuration */
+struct tpm {
+ struct udevice *dev;
+ char inited;
+} tpm;
+
+static struct tpm_chip g_chip;
/*
* iic_tpm_read() - read from TPM register
int count;
uint32_t addrbuf = addr;
- if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) {
+ if ((g_chip.chip_type == SLB9635) || (g_chip.chip_type == UNKNOWN)) {
/* slb9635 protocol should work in both cases */
for (count = 0; count < MAX_COUNT; count++) {
- rc = i2c_write(tpm_dev.addr, 0, 0,
- (uchar *)&addrbuf, 1);
+ rc = dm_i2c_write(g_chip.dev, 0, (uchar *)&addrbuf, 1);
if (rc == 0)
break; /* Success, break to skip sleep */
udelay(SLEEP_DURATION);
*/
for (count = 0; count < MAX_COUNT; count++) {
udelay(SLEEP_DURATION);
- rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len);
+ rc = dm_i2c_read(g_chip.dev, 0, buffer, len);
if (rc == 0)
break; /* success, break to skip sleep */
}
* be safe on the safe side.
*/
for (count = 0; count < MAX_COUNT; count++) {
- rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len);
+ rc = dm_i2c_read(g_chip.dev, addr, buffer, len);
if (rc == 0)
break; /* break here to skip sleep */
udelay(SLEEP_DURATION);
int rc = 0;
int count;
- /* Prepare send buffer */
- tpm_dev.buf[0] = addr;
- memcpy(&(tpm_dev.buf[1]), buffer, len);
-
for (count = 0; count < max_count; count++) {
- rc = i2c_write(tpm_dev.addr, 0, 0, tpm_dev.buf, len + 1);
+ rc = dm_i2c_write(g_chip.dev, addr, buffer, len);
if (rc == 0)
break; /* Success, break to skip sleep */
udelay(sleep_time);
}
/* take care of 'guard time' */
- udelay(SLEEP_DURATION);
+ udelay(sleep_time);
if (rc)
return -rc;
return rc;
if ((buf & mask) == mask) {
- chip->vendor.locality = loc;
+ chip->locality = loc;
return loc;
}
{
unsigned long start, stop;
u8 buf = TPM_ACCESS_REQUEST_USE;
+ int rc;
if (check_locality(chip, loc) >= 0)
return loc; /* We already have the locality */
- iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+ rc = iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+ if (rc)
+ return rc;
/* Wait for burstcount */
start = get_timer(0);
- stop = chip->vendor.timeout_a;
+ stop = chip->timeout_a;
do {
if (check_locality(chip, loc) >= 0)
return loc;
/* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
u8 buf;
- if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+ if (iic_tpm_read(TPM_STS(chip->locality), &buf, 1) < 0)
return 0;
else
return buf;
static void tpm_tis_i2c_ready(struct tpm_chip *chip)
{
+ int rc;
+
/* This causes the current command to be aborted */
u8 buf = TPM_STS_COMMAND_READY;
- iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+ debug("%s\n", __func__);
+ rc = iic_tpm_write_long(TPM_STS(chip->locality), &buf, 1);
+ if (rc)
+ debug("%s: rc=%d\n", __func__, rc);
}
static ssize_t get_burstcount(struct tpm_chip *chip)
/* Wait for burstcount */
/* XXX: Which timeout value? Spec has 2 answers (c & d) */
start = get_timer(0);
- stop = chip->vendor.timeout_d;
+ stop = chip->timeout_d;
do {
/* Note: STS is little endian */
- addr = TPM_STS(chip->vendor.locality) + 1;
+ addr = TPM_STS(chip->locality) + 1;
if (iic_tpm_read(addr, buf, 3) < 0)
burstcnt = 0;
else
if (burstcnt > (count - size))
burstcnt = count - size;
- rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
+ rc = iic_tpm_read(TPM_DATA_FIFO(chip->locality),
&(buf[size]), burstcnt);
if (rc == 0)
size += burstcnt;
expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
if ((size_t)expected > count) {
+ error("Error size=%x, expected=%x, count=%x\n", size, expected,
+ count);
size = -EIO;
goto out;
}
goto out;
}
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+ wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status);
if (status & TPM_STS_DATA_AVAIL) { /* Retry? */
error("Error left over data\n");
size = -EIO;
* so we sleep rather than keeping the bus busy
*/
udelay(2000);
- release_locality(chip, chip->vendor.locality, 0);
+ release_locality(chip, chip->locality, 0);
return size;
}
static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
int rc, status;
- ssize_t burstcnt;
+ size_t burstcnt;
size_t count = 0;
int retry = 0;
u8 sts = TPM_STS_GO;
+ debug("%s: len=%d\n", __func__, len);
if (len > TPM_DEV_BUFSIZE)
return -E2BIG; /* Command is too long for our tpm, sorry */
if ((status & TPM_STS_COMMAND_READY) == 0) {
tpm_tis_i2c_ready(chip);
if (wait_for_stat(chip, TPM_STS_COMMAND_READY,
- chip->vendor.timeout_b, &status) < 0) {
+ chip->timeout_b, &status) < 0) {
rc = -ETIME;
goto out_err;
}
if (burstcnt < 0)
return burstcnt;
- while (count < len - 1) {
- if (burstcnt > len - 1 - count)
- burstcnt = len - 1 - count;
+ while (count < len) {
+ udelay(300);
+ if (burstcnt > len - count)
+ burstcnt = len - count;
#ifdef CONFIG_TPM_TIS_I2C_BURST_LIMITATION
if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION)
burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION;
#endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */
- rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
+ rc = iic_tpm_write(TPM_DATA_FIFO(chip->locality),
&(buf[count]), burstcnt);
if (rc == 0)
count += burstcnt;
else {
- retry++;
- wait_for_stat(chip, TPM_STS_VALID,
- chip->vendor.timeout_c, &status);
+ debug("%s: error\n", __func__);
+ if (retry++ > 10) {
+ rc = -EIO;
+ goto out_err;
+ }
+ rc = wait_for_stat(chip, TPM_STS_VALID,
+ chip->timeout_c, &status);
+ if (rc)
+ goto out_err;
if ((status & TPM_STS_DATA_EXPECT) == 0) {
rc = -EIO;
}
}
- /* Write last byte */
- iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
- if ((status & TPM_STS_DATA_EXPECT) != 0) {
- rc = -EIO;
- goto out_err;
- }
-
/* Go and do it */
- iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+ iic_tpm_write(TPM_STS(chip->locality), &sts, 1);
+ debug("done\n");
return len;
out_err:
+ debug("%s: out_err\n", __func__);
tpm_tis_i2c_ready(chip);
/*
* The TPM needs some time to clean up here,
* so we sleep rather than keeping the bus busy
*/
udelay(2000);
- release_locality(chip, chip->vendor.locality, 0);
+ release_locality(chip, chip->locality, 0);
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,
-};
-
-
-static enum i2c_chip_type tpm_vendor_chip_type(void)
+static enum i2c_chip_type tpm_tis_i2c_chip_type(void)
{
-#ifdef CONFIG_OF_CONTROL
+#if CONFIG_IS_ENABLED(OF_CONTROL)
const void *blob = gd->fdt_blob;
if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9645_TPM) >= 0)
return UNKNOWN;
}
-/* Initialisation of i2c tpm */
-int tpm_vendor_init(uint32_t dev_addr)
+static int tpm_tis_i2c_init(struct udevice *dev)
{
+ struct tpm_chip *chip = &g_chip;
u32 vendor;
u32 expected_did_vid;
- uint 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.chip_type = tpm_vendor_chip_type();
-
- chip = tpm_register_hardware(&tpm_tis_i2c);
- if (chip < 0) {
- rc = -ENODEV;
- goto out_err;
- }
+ g_chip.dev = dev;
+ g_chip.chip_type = tpm_tis_i2c_chip_type();
+ chip->is_open = 1;
/* Disable interrupts (not supported) */
- chip->vendor.irq = 0;
+ chip->irq = 0;
/* Default timeouts */
- chip->vendor.timeout_a = TIS_SHORT_TIMEOUT;
- chip->vendor.timeout_b = TIS_LONG_TIMEOUT;
- chip->vendor.timeout_c = TIS_SHORT_TIMEOUT;
- chip->vendor.timeout_d = TIS_SHORT_TIMEOUT;
-
- if (request_locality(chip, 0) < 0) {
- rc = -ENODEV;
- goto out_err;
- }
+ chip->timeout_a = TIS_SHORT_TIMEOUT;
+ chip->timeout_b = TIS_LONG_TIMEOUT;
+ chip->timeout_c = TIS_SHORT_TIMEOUT;
+ chip->timeout_d = TIS_SHORT_TIMEOUT;
+ chip->req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID;
+ chip->req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID;
+ chip->req_canceled = TPM_STS_COMMAND_READY;
+
+ if (request_locality(chip, 0) < 0)
+ return -ENODEV;
/* Read four bytes from DID_VID register */
if (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
- rc = -EIO;
- goto out_release;
+ release_locality(chip, 0, 1);
+ return -EIO;
}
- if (tpm_dev.chip_type == SLB9635) {
+ if (g_chip.chip_type == SLB9635) {
vendor = be32_to_cpu(vendor);
expected_did_vid = TPM_TIS_I2C_DID_VID_9635;
} else {
expected_did_vid = TPM_TIS_I2C_DID_VID_9645;
}
- if (tpm_dev.chip_type != UNKNOWN && vendor != expected_did_vid) {
+ if (g_chip.chip_type != UNKNOWN && vendor != expected_did_vid) {
error("Vendor id did not match! ID was %08x\n", vendor);
- rc = -ENODEV;
- goto out_release;
+ return -ENODEV;
}
debug("1.2 TPM (chip type %s device-id 0x%X)\n",
- chip_name[tpm_dev.chip_type], vendor >> 16);
+ chip_name[g_chip.chip_type], vendor >> 16);
/*
* A timeout query to TPM can be placed here.
*/
return 0;
+}
-out_release:
- release_locality(chip, 0, 1);
+/* Returns max number of milliseconds to wait */
+static unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
+ u32 ordinal)
+{
+ int duration_idx = TPM_UNDEFINED;
+ int duration = 0;
+
+ if (ordinal < TPM_MAX_ORDINAL) {
+ duration_idx = tpm_ordinal_duration[ordinal];
+ } else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
+ TPM_MAX_PROTECTED_ORDINAL) {
+ duration_idx = tpm_protected_ordinal_duration[
+ ordinal & TPM_PROTECTED_ORDINAL_MASK];
+ }
-out_err:
- tpm_dev.addr = old_addr;
+ if (duration_idx != TPM_UNDEFINED)
+ duration = chip->duration[duration_idx];
+
+ if (duration <= 0)
+ return 2 * 60 * HZ; /* Two minutes timeout */
+ else
+ return duration;
+}
+
+static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
+{
+ int rc;
+ u32 count, ordinal;
+ unsigned long start, stop;
+
+ struct tpm_chip *chip = &g_chip;
+
+ /* switch endianess: big->little */
+ count = get_unaligned_be32(buf + TPM_CMD_COUNT_BYTE);
+ ordinal = get_unaligned_be32(buf + TPM_CMD_ORDINAL_BYTE);
+
+ if (count == 0) {
+ error("no data\n");
+ return -ENODATA;
+ }
+ if (count > bufsiz) {
+ error("invalid count value %x %zx\n", count, bufsiz);
+ return -E2BIG;
+ }
+
+ debug("Calling send\n");
+ rc = tpm_tis_i2c_send(chip, (u8 *)buf, count);
+ debug(" ... done calling send\n");
+ if (rc < 0) {
+ error("tpm_transmit: tpm_send: error %d\n", rc);
+ goto out;
+ }
+
+ if (chip->irq)
+ goto out_recv;
+
+ start = get_timer(0);
+ stop = tpm_calc_ordinal_duration(chip, ordinal);
+ do {
+ debug("waiting for status... %ld %ld\n", start, stop);
+ u8 status = tpm_tis_i2c_status(chip);
+ if ((status & chip->req_complete_mask) ==
+ chip->req_complete_val) {
+ debug("...got it;\n");
+ goto out_recv;
+ }
+
+ if (status == chip->req_canceled) {
+ error("Operation Canceled\n");
+ rc = -ECANCELED;
+ goto out;
+ }
+ udelay(TPM_TIMEOUT * 1000);
+ } while (get_timer(start) < stop);
+
+ tpm_tis_i2c_ready(chip);
+ error("Operation Timed out\n");
+ rc = -ETIME;
+ goto out;
+
+out_recv:
+ debug("out_recv: reading response...\n");
+ rc = tpm_tis_i2c_recv(chip, (u8 *)buf, TPM_BUFSIZE);
+ if (rc < 0)
+ error("tpm_transmit: tpm_recv: error %d\n", rc);
+
+out:
+ return rc;
+}
+
+static int tpm_open_dev(struct udevice *dev)
+{
+ int rc;
+
+ debug("%s: start\n", __func__);
+ if (g_chip.is_open)
+ return -EBUSY;
+ rc = tpm_tis_i2c_init(dev);
+ if (rc < 0)
+ g_chip.is_open = 0;
return rc;
}
-void tpm_vendor_cleanup(struct tpm_chip *chip)
+static void tpm_close(void)
+{
+ if (g_chip.is_open) {
+ release_locality(&g_chip, g_chip.locality, 1);
+ g_chip.is_open = 0;
+ }
+}
+
+/**
+ * Decode TPM configuration.
+ *
+ * @param dev Returns a configuration of TPM device
+ * @return 0 if ok, -1 on error
+ */
+static int tpm_decode_config(struct tpm *dev)
{
- release_locality(chip, chip->vendor.locality, 1);
+ const void *blob = gd->fdt_blob;
+ struct udevice *bus;
+ int chip_addr;
+ int parent;
+ int node;
+ int ret;
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM);
+ if (node < 0) {
+ node = fdtdec_next_compatible(blob, 0,
+ COMPAT_INFINEON_SLB9645_TPM);
+ }
+ if (node < 0) {
+ debug("%s: Node not found\n", __func__);
+ return -1;
+ }
+ parent = fdt_parent_offset(blob, node);
+ if (parent < 0) {
+ debug("%s: Cannot find node parent\n", __func__);
+ return -1;
+ }
+
+ /*
+ * TODO(sjg@chromium.org): Remove this when driver model supports
+ * TPMs
+ */
+ ret = uclass_get_device_by_of_offset(UCLASS_I2C, parent, &bus);
+ if (ret) {
+ debug("Cannot find bus for node '%s: ret=%d'\n",
+ fdt_get_name(blob, parent, NULL), ret);
+ return ret;
+ }
+
+ chip_addr = fdtdec_get_int(blob, node, "reg", -1);
+ if (chip_addr == -1) {
+ debug("Cannot find reg property for node '%s: ret=%d'\n",
+ fdt_get_name(blob, node, NULL), ret);
+ return ret;
+ }
+ /*
+ * TODO(sjg@chromium.org): Older TPMs will need to use the older method
+ * in iic_tpm_read() so the offset length needs to be 0 here.
+ */
+ ret = i2c_get_chip(bus, chip_addr, 1, &dev->dev);
+ if (ret) {
+ debug("Cannot find device for node '%s: ret=%d'\n",
+ fdt_get_name(blob, node, NULL), ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int tis_init(void)
+{
+ if (tpm.inited)
+ return 0;
+
+ if (tpm_decode_config(&tpm))
+ return -1;
+
+ debug("%s: done\n", __func__);
+
+ tpm.inited = 1;
+
+ return 0;
+}
+
+int tis_open(void)
+{
+ int rc;
+
+ if (!tpm.inited)
+ return -1;
+
+ rc = tpm_open_dev(tpm.dev);
+
+ return rc;
+}
+
+int tis_close(void)
+{
+ if (!tpm.inited)
+ return -1;
+
+ tpm_close();
+
+ return 0;
+}
+
+int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
+ uint8_t *recvbuf, size_t *rbuf_len)
+{
+ int len;
+ uint8_t buf[4096];
+
+ if (!tpm.inited)
+ return -1;
+
+ if (sizeof(buf) < sbuf_size)
+ return -1;
+
+ memcpy(buf, sendbuf, sbuf_size);
+
+ len = tpm_transmit(buf, sbuf_size);
+
+ if (len < 10) {
+ *rbuf_len = 0;
+ return -1;
+ }
+
+ memcpy(recvbuf, buf, len);
+ *rbuf_len = len;
+
+ return 0;
}