X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Ftpm%2Ftpm_tis_i2c.c;h=6a3991f9870ea72f2bf089d8638d7f2e267db731;hp=c1bbed4eb5833054c53db2a9b927e9e3144cd6f8;hb=ba651e1ab9a6079a444a54aac7f6ca08667199d0;hpb=98d2d5e8c473232dc718763dbec284b7349dcc05 diff --git a/drivers/tpm/tpm_tis_i2c.c b/drivers/tpm/tpm_tis_i2c.c index c1bbed4eb5..6a3991f987 100644 --- a/drivers/tpm/tpm_tis_i2c.c +++ b/drivers/tpm/tpm_tis_i2c.c @@ -17,26 +17,11 @@ * * 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 +#include #include #include #include @@ -45,13 +30,10 @@ #include #include -#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 @@ -72,13 +54,6 @@ DECLARE_GLOBAL_DATA_PTR; #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, @@ -120,17 +95,311 @@ static const char * const chip_name[] = { #define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) #define TPM_DID_VID(l) (0x0006 | ((l) << 4)) +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 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, +}; + +/* TPM configuration */ +struct tpm { + struct udevice *dev; + char inited; +} tpm; + +/* Global structure for tpm chip data */ +static struct tpm_chip g_chip; + /* Structure to store I2C TPM specific stuff */ struct tpm_dev { - uint addr; + struct udevice *dev; u8 buf[TPM_DEV_BUFSIZE + sizeof(u8)]; /* Max buffer size + addr */ enum i2c_chip_type chip_type; }; -static struct tpm_dev tpm_dev = { - .addr = TPM_I2C_ADDR -}; - static struct tpm_dev tpm_dev; /* @@ -156,8 +425,7 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) 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.addr, 0, 0, - (uchar *)&addrbuf, 1); + rc = dm_i2c_write(tpm_dev.dev, 0, (uchar *)&addrbuf, 1); if (rc == 0) break; /* Success, break to skip sleep */ udelay(SLEEP_DURATION); @@ -171,7 +439,7 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) */ for (count = 0; count < MAX_COUNT; count++) { udelay(SLEEP_DURATION); - rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len); + rc = dm_i2c_read(tpm_dev.dev, 0, buffer, len); if (rc == 0) break; /* success, break to skip sleep */ } @@ -184,7 +452,7 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) * 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(tpm_dev.dev, addr, buffer, len); if (rc == 0) break; /* break here to skip sleep */ udelay(SLEEP_DURATION); @@ -205,19 +473,15 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, 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(tpm_dev.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; @@ -292,11 +556,14 @@ static int request_locality(struct tpm_chip *chip, int 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); @@ -323,10 +590,15 @@ static u8 tpm_tis_i2c_status(struct tpm_chip *chip) 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->vendor.locality), &buf, 1); + if (rc) + debug("%s: rc=%d\n", __func__, rc); } static ssize_t get_burstcount(struct tpm_chip *chip) @@ -422,6 +694,8 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) 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; } @@ -456,11 +730,12 @@ out: 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 */ @@ -483,9 +758,10 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) 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) @@ -497,9 +773,15 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) 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->vendor.timeout_c, &status); + if (rc) + goto out_err; if ((status & TPM_STS_DATA_EXPECT) == 0) { rc = -EIO; @@ -508,20 +790,14 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) } } - /* 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); + 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, @@ -534,10 +810,6 @@ out_err: } 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, @@ -546,7 +818,7 @@ static struct tpm_vendor_specific tpm_tis_i2c = { static enum i2c_chip_type tpm_vendor_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) @@ -558,26 +830,18 @@ static enum i2c_chip_type tpm_vendor_chip_type(void) return UNKNOWN; } -/* Initialisation of i2c tpm */ -int tpm_vendor_init(uint32_t dev_addr) +int tpm_vendor_init(struct udevice *dev) { + struct tpm_chip *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.dev = dev; tpm_dev.chip_type = tpm_vendor_chip_type(); chip = tpm_register_hardware(&tpm_tis_i2c); - if (chip < 0) { - rc = -ENODEV; - goto out_err; - } + if (chip < 0) + return -ENODEV; /* Disable interrupts (not supported) */ chip->vendor.irq = 0; @@ -588,15 +852,13 @@ int tpm_vendor_init(uint32_t dev_addr) 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; - } + 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) { @@ -609,8 +871,7 @@ int tpm_vendor_init(uint32_t dev_addr) if (tpm_dev.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", @@ -622,16 +883,257 @@ int tpm_vendor_init(uint32_t dev_addr) */ return 0; +} + +void tpm_vendor_cleanup(struct tpm_chip *chip) +{ + release_locality(chip, chip->vendor.locality, 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_release: - release_locality(chip, 0, 1); + if (duration_idx != TPM_UNDEFINED) + duration = chip->vendor.duration[duration_idx]; -out_err: - tpm_dev.addr = old_addr; + 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->vendor.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->vendor.req_complete_mask) == + chip->vendor.req_complete_val) { + debug("...got it;\n"); + goto out_recv; + } + + if (status == chip->vendor.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; } -void tpm_vendor_cleanup(struct tpm_chip *chip) +static int tpm_open_dev(struct udevice *dev) { - release_locality(chip, chip->vendor.locality, 1); + int rc; + + debug("%s: start\n", __func__); + if (g_chip.is_open) + return -EBUSY; + rc = tpm_vendor_init(dev); + if (rc < 0) + g_chip.is_open = 0; + return rc; +} + +static void tpm_close(void) +{ + if (g_chip.is_open) { + tpm_vendor_cleanup(&g_chip); + 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) +{ + 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; +} + +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 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; }