]> git.sur5r.net Git - u-boot/commitdiff
dm: tpm: Move tpm_tis_i2c to tpm_i2c_infineon
authorChristophe Ricard <christophe.ricard@gmail.com>
Tue, 6 Oct 2015 20:54:41 +0000 (22:54 +0200)
committerSimon Glass <sjg@chromium.org>
Fri, 23 Oct 2015 15:42:28 +0000 (09:42 -0600)
As there is no TCG specification or recommendation for i2c TPM 1.2,
move tpm_tis_i2c driver to tpm_i2c_infineon. Other tpm vendors like Atmel
or STMicroelectronics may have a different transport protocol for i2c.

Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
Acked-by: Simon Glass <sjg@chromium.org>
12 files changed:
README
configs/nyan-big_defconfig
configs/peach-pi_defconfig
configs/peach-pit_defconfig
configs/snow_defconfig
configs/spring_defconfig
drivers/tpm/Kconfig
drivers/tpm/Makefile
drivers/tpm/tpm_tis_i2c.c [deleted file]
drivers/tpm/tpm_tis_i2c.h [deleted file]
drivers/tpm/tpm_tis_infineon.c [new file with mode: 0644]
drivers/tpm/tpm_tis_infineon.h [new file with mode: 0644]

diff --git a/README b/README
index d18df54ec9a14d662f4a6495b45aa92c61aa9962..63bdeb9930aeccc1dde1801845aa04609696698f 100644 (file)
--- a/README
+++ b/README
@@ -1472,8 +1472,8 @@ The following options need to be configured:
                CONFIG_TPM
                Support TPM devices.
 
-               CONFIG_TPM_TIS_I2C
-               Support for i2c bus TPM devices. Only one device
+               CONFIG_TPM_TIS_INFINEON
+               Support for Infineon i2c bus TPM devices. Only one device
                per system is supported at this time.
 
                        CONFIG_TPM_TIS_I2C_BURST_LIMITATION
index 2dd293007b82528aa7cc02c9abd3be6ad99ef2e2..4f31c1c2203931759d431f9f204578f1b1147c89 100644 (file)
@@ -20,7 +20,7 @@ CONFIG_CROS_EC_SPI=y
 CONFIG_SPI_FLASH=y
 CONFIG_TEGRA114_SPI=y
 CONFIG_DM_TPM=y
-CONFIG_TPM_TIS_I2C=y
+CONFIG_TPM_TIS_INFINEON=y
 CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_DISPLAY_PORT=y
index dfdf61b07231bf06ba62f07fbc3cefd456d93e57..6eff072a13cf8aa4d24e179594b6a5a3059fbc15 100644 (file)
@@ -30,7 +30,7 @@ CONFIG_I2S_SAMSUNG=y
 CONFIG_SOUND_MAX98095=y
 CONFIG_SOUND_WM8994=y
 CONFIG_DM_TPM=y
-CONFIG_TPM_TIS_I2C=y
+CONFIG_TPM_TIS_INFINEON=y
 CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_VIDEO_BRIDGE=y
index 01c4f37b093b3c55c3f573deb70a1849276fa918..24bc161374ee9c783992ca31c9184c3023b5e432 100644 (file)
@@ -30,7 +30,7 @@ CONFIG_I2S_SAMSUNG=y
 CONFIG_SOUND_MAX98095=y
 CONFIG_SOUND_WM8994=y
 CONFIG_DM_TPM=y
-CONFIG_TPM_TIS_I2C=y
+CONFIG_TPM_TIS_INFINEON=y
 CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_VIDEO_BRIDGE=y
index c4e2f61e28f98ca6843a4e19a16e4793a3d1763f..0c9ca8b5a41ed803447b56e05e1c63b6b1e188ff 100644 (file)
@@ -39,7 +39,7 @@ CONFIG_I2S_SAMSUNG=y
 CONFIG_SOUND_MAX98095=y
 CONFIG_SOUND_WM8994=y
 CONFIG_DM_TPM=y
-CONFIG_TPM_TIS_I2C=y
+CONFIG_TPM_TIS_INFINEON=y
 CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_VIDEO_BRIDGE=y
index 4acadf34ef893c8f8910f61fd0cff611d2c07ca8..47bc6438dada031ad341242d21c6173ea361e9df 100644 (file)
@@ -39,7 +39,7 @@ CONFIG_I2S_SAMSUNG=y
 CONFIG_SOUND_MAX98095=y
 CONFIG_SOUND_WM8994=y
 CONFIG_DM_TPM=y
-CONFIG_TPM_TIS_I2C=y
+CONFIG_TPM_TIS_INFINEON=y
 CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_VIDEO_BRIDGE=y
index 6bc8fddbd16b7368d2b43e611e0cac01935c2f47..dacb847bdc344182e2b47c0c5d8296d58cbf54a5 100644 (file)
@@ -31,7 +31,7 @@ config TPM_ATMEL_TWI
          to the device using the standard TPM Interface Specification (TIS)
          protocol
 
-config TPM_TIS_I2C
+config TPM_TIS_INFINEON
        bool "Enable support for Infineon SLB9635/45 TPMs on I2C"
        depends on TPM && DM_I2C
        help
@@ -42,7 +42,7 @@ config TPM_TIS_I2C
 
 config TPM_TIS_I2C_BURST_LIMITATION
        bool "Enable I2C burst length limitation"
-       depends on TPM_TIS_I2C
+       depends on TPM_TIS_INFINEON
        help
          Some broken TPMs have a limitation on the number of bytes they can
          receive in one message. Enable this option to allow you to set this
index 0d328f8d9d35110a0037400e71ea51a4df7906dc..5748145ad59e5ba44eb3d1da68a55afd4a060724 100644 (file)
@@ -6,6 +6,6 @@
 obj-$(CONFIG_DM_TPM) += tpm-uclass.o
 
 obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o
-obj-$(CONFIG_TPM_TIS_I2C) += tpm_tis_i2c.o
+obj-$(CONFIG_TPM_TIS_INFINEON) += tpm_tis_infineon.o
 obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o
 obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
diff --git a/drivers/tpm/tpm_tis_i2c.c b/drivers/tpm/tpm_tis_i2c.c
deleted file mode 100644 (file)
index 9afe46c..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Copyright (C) 2011 Infineon Technologies
- *
- * Authors:
- * Peter Huewe <huewe.external@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
- *
- * SPDX-License-Identifier:    GPL-2.0
- */
-
-#include <common.h>
-#include <dm.h>
-#include <fdtdec.h>
-#include <i2c.h>
-#include <tis.h>
-#include <tpm.h>
-#include <asm-generic/errno.h>
-#include <linux/compiler.h>
-#include <linux/types.h>
-#include <linux/unaligned/be_byteshift.h>
-
-#include "tpm_tis_i2c.h"
-#include "tpm_internal.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static const char * const chip_name[] = {
-       [SLB9635] = "slb9635tt",
-       [SLB9645] = "slb9645tt",
-       [UNKNOWN] = "unknown/fallback to slb9635",
-};
-
-/*
- * tpm_tis_i2c_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 tpm_tis_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
-                           size_t len)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       int rc;
-       int count;
-       uint32_t addrbuf = addr;
-
-       if ((chip->chip_type == SLB9635) || (chip->chip_type == UNKNOWN)) {
-               /* slb9635 protocol should work in both cases */
-               for (count = 0; count < MAX_COUNT; count++) {
-                       rc = dm_i2c_write(dev, 0, (uchar *)&addrbuf, 1);
-                       if (rc == 0)
-                               break;  /* Success, break to skip sleep */
-                       udelay(SLEEP_DURATION_US);
-               }
-               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_US);
-                       rc = dm_i2c_read(dev, 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 = dm_i2c_read(dev, addr, buffer, len);
-                       if (rc == 0)
-                               break;  /* break here to skip sleep */
-                       udelay(SLEEP_DURATION_US);
-               }
-       }
-
-       /* Take care of 'guard time' */
-       udelay(SLEEP_DURATION_US);
-       if (rc)
-               return rc;
-
-       return 0;
-}
-
-static int tpm_tis_i2c_write_generic(struct udevice *dev, u8 addr,
-                                    const u8 *buffer, size_t len,
-                                    unsigned int sleep_time_us, u8 max_count)
-{
-       struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
-       struct tpm_chip *chip = dev_get_priv(dev);
-       int rc = 0;
-       int count;
-
-       if (chip->chip_type == SLB9635) {
-               /* Prepare send buffer to include the address */
-               priv->buf[0] = addr;
-               memcpy(&(priv->buf[1]), buffer, len);
-               buffer = priv->buf;
-               len++;
-               addr = 0;
-       }
-
-       for (count = 0; count < max_count; count++) {
-               rc = dm_i2c_write(dev, addr, buffer, len);
-               if (rc == 0)
-                       break;  /* Success, break to skip sleep */
-               udelay(sleep_time_us);
-       }
-
-       /* take care of 'guard time' */
-       udelay(sleep_time_us);
-       if (rc)
-               return rc;
-
-       return 0;
-}
-
-/*
- * tpm_tis_i2c_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 tpm_tis_i2c_write_generic function.
- *
- * Return -EIO on error, 0 on success
- */
-static int tpm_tis_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
-                            size_t len)
-{
-       return tpm_tis_i2c_write_generic(dev, addr, buffer, len,
-                                        SLEEP_DURATION_US, MAX_COUNT);
-}
-
-/*
- * This function is needed especially for the cleanup situation after
- * sending TPM_READY
- */
-static int tpm_tis_i2c_write_long(struct udevice *dev, u8 addr, u8 *buffer,
-                                 size_t len)
-{
-       return tpm_tis_i2c_write_generic(dev, addr, buffer, len,
-                                        SLEEP_DURATION_LONG_US,
-                                        MAX_COUNT_LONG);
-}
-
-static int tpm_tis_i2c_check_locality(struct udevice *dev, int loc)
-{
-       const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
-       struct tpm_chip *chip = dev_get_priv(dev);
-       u8 buf;
-       int rc;
-
-       rc = tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1);
-       if (rc < 0)
-               return rc;
-
-       if ((buf & mask) == mask) {
-               chip->locality = loc;
-               return loc;
-       }
-
-       return -ENOENT;
-}
-
-static void tpm_tis_i2c_release_locality(struct udevice *dev, int loc,
-                                        int force)
-{
-       const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
-       u8 buf;
-
-       if (tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1) < 0)
-               return;
-
-       if (force || (buf & mask) == mask) {
-               buf = TPM_ACCESS_ACTIVE_LOCALITY;
-               tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1);
-       }
-}
-
-static int tpm_tis_i2c_request_locality(struct udevice *dev, int loc)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       unsigned long start, stop;
-       u8 buf = TPM_ACCESS_REQUEST_USE;
-       int rc;
-
-       rc = tpm_tis_i2c_check_locality(dev, loc);
-       if (rc >= 0) {
-               debug("%s: Already have locality\n", __func__);
-               return loc;  /* We already have the locality */
-       } else if (rc != -ENOENT) {
-               debug("%s: Failed to get locality: %d\n", __func__, rc);
-               return rc;
-       }
-
-       rc = tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1);
-       if (rc) {
-               debug("%s: Failed to write to TPM: %d\n", __func__, rc);
-               return rc;
-       }
-
-       /* Wait for burstcount */
-       start = get_timer(0);
-       stop = chip->timeout_a;
-       do {
-               rc = tpm_tis_i2c_check_locality(dev, loc);
-               if (rc >= 0) {
-                       debug("%s: Have locality\n", __func__);
-                       return loc;
-               } else if (rc != -ENOENT) {
-                       debug("%s: Failed to get locality: %d\n", __func__, rc);
-                       return rc;
-               }
-               mdelay(TPM_TIMEOUT_MS);
-       } while (get_timer(start) < stop);
-       debug("%s: Timeout getting locality: %d\n", __func__, rc);
-
-       return rc;
-}
-
-static u8 tpm_tis_i2c_status(struct udevice *dev)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
-       u8 buf;
-
-       if (tpm_tis_i2c_read(dev, TPM_STS(chip->locality), &buf, 1) < 0)
-               return 0;
-       else
-               return buf;
-}
-
-static int tpm_tis_i2c_ready(struct udevice *dev)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       int rc;
-
-       /* This causes the current command to be aborted */
-       u8 buf = TPM_STS_COMMAND_READY;
-
-       debug("%s\n", __func__);
-       rc = tpm_tis_i2c_write_long(dev, TPM_STS(chip->locality), &buf, 1);
-       if (rc)
-               debug("%s: rc=%d\n", __func__, rc);
-
-       return rc;
-}
-
-static ssize_t tpm_tis_i2c_get_burstcount(struct udevice *dev)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       unsigned long start, stop;
-       ssize_t burstcnt;
-       u8 addr, buf[3];
-
-       /* Wait for burstcount */
-       /* XXX: Which timeout value? Spec has 2 answers (c & d) */
-       start = get_timer(0);
-       stop = chip->timeout_d;
-       do {
-               /* Note: STS is little endian */
-               addr = TPM_STS(chip->locality) + 1;
-               if (tpm_tis_i2c_read(dev, addr, buf, 3) < 0)
-                       burstcnt = 0;
-               else
-                       burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
-
-               if (burstcnt)
-                       return burstcnt;
-               mdelay(TPM_TIMEOUT_MS);
-       } while (get_timer(start) < stop);
-
-       return -EBUSY;
-}
-
-static int tpm_tis_i2c_wait_for_stat(struct udevice *dev, u8 mask,
-                                    unsigned long timeout, int *status)
-{
-       unsigned long start, stop;
-
-       /* Check current status */
-       *status = tpm_tis_i2c_status(dev);
-       if ((*status & mask) == mask)
-               return 0;
-
-       start = get_timer(0);
-       stop = timeout;
-       do {
-               mdelay(TPM_TIMEOUT_MS);
-               *status = tpm_tis_i2c_status(dev);
-               if ((*status & mask) == mask)
-                       return 0;
-       } while (get_timer(start) < stop);
-
-       return -ETIMEDOUT;
-}
-
-static int tpm_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       size_t size = 0;
-       ssize_t burstcnt;
-       int rc;
-
-       while (size < count) {
-               burstcnt = tpm_tis_i2c_get_burstcount(dev);
-
-               /* burstcount < 0 -> tpm is busy */
-               if (burstcnt < 0)
-                       return burstcnt;
-
-               /* Limit received data to max left */
-               if (burstcnt > (count - size))
-                       burstcnt = count - size;
-
-               rc = tpm_tis_i2c_read(dev, TPM_DATA_FIFO(chip->locality),
-                                     &(buf[size]), burstcnt);
-               if (rc == 0)
-                       size += burstcnt;
-       }
-
-       return size;
-}
-
-static int tpm_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       int size = 0;
-       int expected, status;
-       int rc;
-
-       status = tpm_tis_i2c_status(dev);
-       if (status == TPM_STS_COMMAND_READY)
-               return -EINTR;
-       if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) !=
-           (TPM_STS_DATA_AVAIL | TPM_STS_VALID))
-               return -EAGAIN;
-
-       debug("...got it;\n");
-
-       /* Read first 10 bytes, including tag, paramsize, and result */
-       size = tpm_tis_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
-       if (size < TPM_HEADER_SIZE) {
-               debug("Unable to read header\n");
-               return size < 0 ? size : -EIO;
-       }
-
-       expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
-       if ((size_t)expected > count) {
-               debug("Error size=%x, expected=%x, count=%x\n", size, expected,
-                     count);
-               return -ENOSPC;
-       }
-
-       size += tpm_tis_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
-                                     expected - TPM_HEADER_SIZE);
-       if (size < expected) {
-               debug("Unable to read remainder of result\n");
-               return -ETIMEDOUT;
-       }
-
-       rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c,
-                                      &status);
-       if (rc)
-               return rc;
-       if (status & TPM_STS_DATA_AVAIL) {  /* Retry? */
-               debug("Error left over data\n");
-               return -EIO;
-       }
-
-       return size;
-}
-
-static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       int rc, status;
-       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 (tpm_tis_i2c_request_locality(dev, 0) < 0)
-               return -EBUSY;
-
-       status = tpm_tis_i2c_status(dev);
-       if ((status & TPM_STS_COMMAND_READY) == 0) {
-               rc = tpm_tis_i2c_ready(dev);
-               if (rc)
-                       return rc;
-               rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
-                                              chip->timeout_b, &status);
-               if (rc)
-                       return rc;
-       }
-
-       burstcnt = tpm_tis_i2c_get_burstcount(dev);
-
-       /* burstcount < 0 -> tpm is busy */
-       if (burstcnt < 0)
-               return burstcnt;
-
-       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_LEN)
-                       burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION_LEN;
-#endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */
-
-               rc = tpm_tis_i2c_write(dev, TPM_DATA_FIFO(chip->locality),
-                                      &(buf[count]), burstcnt);
-               if (rc == 0)
-                       count += burstcnt;
-               else {
-                       debug("%s: error\n", __func__);
-                       if (retry++ > 10)
-                               return -EIO;
-                       rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID,
-                                                      chip->timeout_c,
-                                                      &status);
-                       if (rc)
-                               return rc;
-
-                       if ((status & TPM_STS_DATA_EXPECT) == 0)
-                               return -EIO;
-               }
-       }
-
-       /* Go and do it */
-       rc = tpm_tis_i2c_write(dev, TPM_STS(chip->locality), &sts, 1);
-       if (rc < 0)
-               return rc;
-       debug("%s: done, rc=%d\n", __func__, rc);
-
-       return len;
-}
-
-static int tpm_tis_i2c_cleanup(struct udevice *dev)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-
-       tpm_tis_i2c_ready(dev);
-       /*
-        * The TPM needs some time to clean up here,
-        * so we sleep rather than keeping the bus busy
-        */
-       mdelay(2);
-       tpm_tis_i2c_release_locality(dev, chip->locality, 0);
-
-       return 0;
-}
-
-static int tpm_tis_i2c_init(struct udevice *dev)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       u32 vendor;
-       u32 expected_did_vid;
-       int rc;
-
-       chip->is_open = 1;
-
-       /* Default timeouts - these could move to the device tree */
-       chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
-       chip->timeout_b = TIS_LONG_TIMEOUT_MS;
-       chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
-       chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
-
-       rc = tpm_tis_i2c_request_locality(dev, 0);
-       if (rc < 0)
-               return rc;
-
-       /* Read four bytes from DID_VID register */
-       if (tpm_tis_i2c_read(dev, TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
-               tpm_tis_i2c_release_locality(dev, 0, 1);
-               return -EIO;
-       }
-
-       if (chip->chip_type == SLB9635) {
-               vendor = be32_to_cpu(vendor);
-               expected_did_vid = TPM_TIS_I2C_DID_VID_9635;
-       } else {
-               /* device id and byte order has changed for newer i2c tpms */
-               expected_did_vid = TPM_TIS_I2C_DID_VID_9645;
-       }
-
-       if (chip->chip_type != UNKNOWN && vendor != expected_did_vid) {
-               error("Vendor id did not match! ID was %08x\n", vendor);
-               return -ENODEV;
-       }
-
-       chip->vend_dev = vendor;
-       debug("1.2 TPM (chip type %s device-id 0x%X)\n",
-             chip_name[chip->chip_type], vendor >> 16);
-
-       /*
-        * A timeout query to TPM can be placed here.
-        * Standard timeout values are used so far
-        */
-
-       return 0;
-}
-
-static int tpm_tis_i2c_open(struct udevice *dev)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-       int rc;
-
-       debug("%s: start\n", __func__);
-       if (chip->is_open)
-               return -EBUSY;
-       rc = tpm_tis_i2c_init(dev);
-       if (rc < 0)
-               chip->is_open = 0;
-
-       return rc;
-}
-
-static int tpm_tis_i2c_close(struct udevice *dev)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-
-       if (chip->is_open) {
-               tpm_tis_i2c_release_locality(dev, chip->locality, 1);
-               chip->is_open = 0;
-               chip->vend_dev = 0;
-       }
-
-       return 0;
-}
-
-static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
-{
-       struct tpm_chip *chip = dev_get_priv(dev);
-
-       if (size < 50)
-               return -ENOSPC;
-
-       return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)",
-                       chip->is_open ? "open" : "closed",
-                       chip_name[chip->chip_type],
-                       chip->vend_dev >> 16);
-}
-
-static int tpm_tis_i2c_probe(struct udevice *dev)
-{
-       struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
-       struct tpm_chip *chip = dev_get_priv(dev);
-
-       chip->chip_type = dev_get_driver_data(dev);
-
-       /* TODO: These need to be checked and tuned */
-       uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
-       uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
-       uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
-       uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
-
-       return 0;
-}
-
-static const struct tpm_ops tpm_tis_i2c_ops = {
-       .open           = tpm_tis_i2c_open,
-       .close          = tpm_tis_i2c_close,
-       .get_desc       = tpm_tis_get_desc,
-       .send           = tpm_tis_i2c_send,
-       .recv           = tpm_tis_i2c_recv,
-       .cleanup        = tpm_tis_i2c_cleanup,
-};
-
-static const struct udevice_id tpm_tis_i2c_ids[] = {
-       { .compatible = "infineon,slb9635tt", .data = SLB9635 },
-       { .compatible = "infineon,slb9645tt", .data = SLB9645 },
-       { }
-};
-
-U_BOOT_DRIVER(tpm_tis_i2c) = {
-       .name   = "tpm_tis_i2c",
-       .id     = UCLASS_TPM,
-       .of_match = tpm_tis_i2c_ids,
-       .ops    = &tpm_tis_i2c_ops,
-       .probe  = tpm_tis_i2c_probe,
-       .priv_auto_alloc_size = sizeof(struct tpm_chip),
-};
diff --git a/drivers/tpm/tpm_tis_i2c.h b/drivers/tpm/tpm_tis_i2c.h
deleted file mode 100644 (file)
index 3b510d1..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2011 Infineon Technologies
- *
- * Authors:
- * Peter Huewe <huewe.external@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.
- *
- * SPDX-License-Identifier:    GPL-2.0
- */
-
-#ifndef _TPM_TIS_I2C_H
-#define _TPM_TIS_I2C_H
-
-#include <linux/compiler.h>
-#include <linux/types.h>
-
-enum tpm_timeout {
-       TPM_TIMEOUT_MS                  = 5,
-       TIS_SHORT_TIMEOUT_MS            = 750,
-       TIS_LONG_TIMEOUT_MS             = 2000,
-       SLEEP_DURATION_US               = 60,
-       SLEEP_DURATION_LONG_US          = 210,
-};
-
-/* Size of external transmit buffer (used in tpm_transmit)*/
-#define TPM_BUFSIZE 4096
-
-/* Index of Count field in TPM response buffer */
-#define TPM_RSP_SIZE_BYTE      2
-#define TPM_RSP_RC_BYTE                6
-
-enum i2c_chip_type {
-       SLB9635,
-       SLB9645,
-       UNKNOWN,
-};
-
-struct tpm_chip {
-       int is_open;
-       int locality;
-       u32 vend_dev;
-       unsigned long timeout_a, timeout_b, timeout_c, timeout_d;  /* msec */
-       enum i2c_chip_type chip_type;
-};
-
-struct tpm_input_header {
-       __be16 tag;
-       __be32 length;
-       __be32 ordinal;
-} __packed;
-
-struct tpm_output_header {
-       __be16 tag;
-       __be32 length;
-       __be32 return_code;
-} __packed;
-
-struct timeout_t {
-       __be32 a;
-       __be32 b;
-       __be32 c;
-       __be32 d;
-} __packed;
-
-struct duration_t {
-       __be32 tpm_short;
-       __be32 tpm_medium;
-       __be32 tpm_long;
-} __packed;
-
-union cap_t {
-       struct timeout_t timeout;
-       struct duration_t duration;
-};
-
-struct tpm_getcap_params_in {
-       __be32 cap;
-       __be32 subcap_size;
-       __be32 subcap;
-} __packed;
-
-struct tpm_getcap_params_out {
-       __be32 cap_size;
-       union cap_t cap;
-} __packed;
-
-union tpm_cmd_header {
-       struct tpm_input_header in;
-       struct tpm_output_header out;
-};
-
-union tpm_cmd_params {
-       struct tpm_getcap_params_out getcap_out;
-       struct tpm_getcap_params_in getcap_in;
-};
-
-struct tpm_cmd_t {
-       union tpm_cmd_header header;
-       union tpm_cmd_params params;
-} __packed;
-
-/* Max number of iterations after i2c NAK */
-#define MAX_COUNT              3
-
-/*
- * 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
-
-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,
-};
-
-/* expected value for DIDVID register */
-#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
-#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
-
-#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))
-
-#endif
diff --git a/drivers/tpm/tpm_tis_infineon.c b/drivers/tpm/tpm_tis_infineon.c
new file mode 100644 (file)
index 0000000..88eb786
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external@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
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <tis.h>
+#include <tpm.h>
+#include <asm-generic/errno.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/unaligned/be_byteshift.h>
+
+#include "tpm_tis_infineon.h"
+#include "tpm_internal.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char * const chip_name[] = {
+       [SLB9635] = "slb9635tt",
+       [SLB9645] = "slb9645tt",
+       [UNKNOWN] = "unknown/fallback to slb9635",
+};
+
+/*
+ * tpm_tis_i2c_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 tpm_tis_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
+                           size_t len)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       int rc;
+       int count;
+       uint32_t addrbuf = addr;
+
+       if ((chip->chip_type == SLB9635) || (chip->chip_type == UNKNOWN)) {
+               /* slb9635 protocol should work in both cases */
+               for (count = 0; count < MAX_COUNT; count++) {
+                       rc = dm_i2c_write(dev, 0, (uchar *)&addrbuf, 1);
+                       if (rc == 0)
+                               break;  /* Success, break to skip sleep */
+                       udelay(SLEEP_DURATION_US);
+               }
+               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_US);
+                       rc = dm_i2c_read(dev, 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 = dm_i2c_read(dev, addr, buffer, len);
+                       if (rc == 0)
+                               break;  /* break here to skip sleep */
+                       udelay(SLEEP_DURATION_US);
+               }
+       }
+
+       /* Take care of 'guard time' */
+       udelay(SLEEP_DURATION_US);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
+static int tpm_tis_i2c_write_generic(struct udevice *dev, u8 addr,
+                                    const u8 *buffer, size_t len,
+                                    unsigned int sleep_time_us, u8 max_count)
+{
+       struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
+       struct tpm_chip *chip = dev_get_priv(dev);
+       int rc = 0;
+       int count;
+
+       if (chip->chip_type == SLB9635) {
+               /* Prepare send buffer to include the address */
+               priv->buf[0] = addr;
+               memcpy(&(priv->buf[1]), buffer, len);
+               buffer = priv->buf;
+               len++;
+               addr = 0;
+       }
+
+       for (count = 0; count < max_count; count++) {
+               rc = dm_i2c_write(dev, addr, buffer, len);
+               if (rc == 0)
+                       break;  /* Success, break to skip sleep */
+               udelay(sleep_time_us);
+       }
+
+       /* take care of 'guard time' */
+       udelay(sleep_time_us);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
+/*
+ * tpm_tis_i2c_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 tpm_tis_i2c_write_generic function.
+ *
+ * Return -EIO on error, 0 on success
+ */
+static int tpm_tis_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
+                            size_t len)
+{
+       return tpm_tis_i2c_write_generic(dev, addr, buffer, len,
+                                        SLEEP_DURATION_US, MAX_COUNT);
+}
+
+/*
+ * This function is needed especially for the cleanup situation after
+ * sending TPM_READY
+ */
+static int tpm_tis_i2c_write_long(struct udevice *dev, u8 addr, u8 *buffer,
+                                 size_t len)
+{
+       return tpm_tis_i2c_write_generic(dev, addr, buffer, len,
+                                        SLEEP_DURATION_LONG_US,
+                                        MAX_COUNT_LONG);
+}
+
+static int tpm_tis_i2c_check_locality(struct udevice *dev, int loc)
+{
+       const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
+       struct tpm_chip *chip = dev_get_priv(dev);
+       u8 buf;
+       int rc;
+
+       rc = tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1);
+       if (rc < 0)
+               return rc;
+
+       if ((buf & mask) == mask) {
+               chip->locality = loc;
+               return loc;
+       }
+
+       return -ENOENT;
+}
+
+static void tpm_tis_i2c_release_locality(struct udevice *dev, int loc,
+                                        int force)
+{
+       const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
+       u8 buf;
+
+       if (tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1) < 0)
+               return;
+
+       if (force || (buf & mask) == mask) {
+               buf = TPM_ACCESS_ACTIVE_LOCALITY;
+               tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1);
+       }
+}
+
+static int tpm_tis_i2c_request_locality(struct udevice *dev, int loc)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       unsigned long start, stop;
+       u8 buf = TPM_ACCESS_REQUEST_USE;
+       int rc;
+
+       rc = tpm_tis_i2c_check_locality(dev, loc);
+       if (rc >= 0) {
+               debug("%s: Already have locality\n", __func__);
+               return loc;  /* We already have the locality */
+       } else if (rc != -ENOENT) {
+               debug("%s: Failed to get locality: %d\n", __func__, rc);
+               return rc;
+       }
+
+       rc = tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1);
+       if (rc) {
+               debug("%s: Failed to write to TPM: %d\n", __func__, rc);
+               return rc;
+       }
+
+       /* Wait for burstcount */
+       start = get_timer(0);
+       stop = chip->timeout_a;
+       do {
+               rc = tpm_tis_i2c_check_locality(dev, loc);
+               if (rc >= 0) {
+                       debug("%s: Have locality\n", __func__);
+                       return loc;
+               } else if (rc != -ENOENT) {
+                       debug("%s: Failed to get locality: %d\n", __func__, rc);
+                       return rc;
+               }
+               mdelay(TPM_TIMEOUT_MS);
+       } while (get_timer(start) < stop);
+       debug("%s: Timeout getting locality: %d\n", __func__, rc);
+
+       return rc;
+}
+
+static u8 tpm_tis_i2c_status(struct udevice *dev)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       /* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
+       u8 buf;
+
+       if (tpm_tis_i2c_read(dev, TPM_STS(chip->locality), &buf, 1) < 0)
+               return 0;
+       else
+               return buf;
+}
+
+static int tpm_tis_i2c_ready(struct udevice *dev)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       int rc;
+
+       /* This causes the current command to be aborted */
+       u8 buf = TPM_STS_COMMAND_READY;
+
+       debug("%s\n", __func__);
+       rc = tpm_tis_i2c_write_long(dev, TPM_STS(chip->locality), &buf, 1);
+       if (rc)
+               debug("%s: rc=%d\n", __func__, rc);
+
+       return rc;
+}
+
+static ssize_t tpm_tis_i2c_get_burstcount(struct udevice *dev)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       unsigned long start, stop;
+       ssize_t burstcnt;
+       u8 addr, buf[3];
+
+       /* Wait for burstcount */
+       /* XXX: Which timeout value? Spec has 2 answers (c & d) */
+       start = get_timer(0);
+       stop = chip->timeout_d;
+       do {
+               /* Note: STS is little endian */
+               addr = TPM_STS(chip->locality) + 1;
+               if (tpm_tis_i2c_read(dev, addr, buf, 3) < 0)
+                       burstcnt = 0;
+               else
+                       burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+               if (burstcnt)
+                       return burstcnt;
+               mdelay(TPM_TIMEOUT_MS);
+       } while (get_timer(start) < stop);
+
+       return -EBUSY;
+}
+
+static int tpm_tis_i2c_wait_for_stat(struct udevice *dev, u8 mask,
+                                    unsigned long timeout, int *status)
+{
+       unsigned long start, stop;
+
+       /* Check current status */
+       *status = tpm_tis_i2c_status(dev);
+       if ((*status & mask) == mask)
+               return 0;
+
+       start = get_timer(0);
+       stop = timeout;
+       do {
+               mdelay(TPM_TIMEOUT_MS);
+               *status = tpm_tis_i2c_status(dev);
+               if ((*status & mask) == mask)
+                       return 0;
+       } while (get_timer(start) < stop);
+
+       return -ETIMEDOUT;
+}
+
+static int tpm_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       size_t size = 0;
+       ssize_t burstcnt;
+       int rc;
+
+       while (size < count) {
+               burstcnt = tpm_tis_i2c_get_burstcount(dev);
+
+               /* burstcount < 0 -> tpm is busy */
+               if (burstcnt < 0)
+                       return burstcnt;
+
+               /* Limit received data to max left */
+               if (burstcnt > (count - size))
+                       burstcnt = count - size;
+
+               rc = tpm_tis_i2c_read(dev, TPM_DATA_FIFO(chip->locality),
+                                     &(buf[size]), burstcnt);
+               if (rc == 0)
+                       size += burstcnt;
+       }
+
+       return size;
+}
+
+static int tpm_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       int size = 0;
+       int expected, status;
+       int rc;
+
+       status = tpm_tis_i2c_status(dev);
+       if (status == TPM_STS_COMMAND_READY)
+               return -EINTR;
+       if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) !=
+           (TPM_STS_DATA_AVAIL | TPM_STS_VALID))
+               return -EAGAIN;
+
+       debug("...got it;\n");
+
+       /* Read first 10 bytes, including tag, paramsize, and result */
+       size = tpm_tis_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
+       if (size < TPM_HEADER_SIZE) {
+               debug("Unable to read header\n");
+               return size < 0 ? size : -EIO;
+       }
+
+       expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
+       if ((size_t)expected > count) {
+               debug("Error size=%x, expected=%x, count=%x\n", size, expected,
+                     count);
+               return -ENOSPC;
+       }
+
+       size += tpm_tis_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
+                                     expected - TPM_HEADER_SIZE);
+       if (size < expected) {
+               debug("Unable to read remainder of result\n");
+               return -ETIMEDOUT;
+       }
+
+       rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c,
+                                      &status);
+       if (rc)
+               return rc;
+       if (status & TPM_STS_DATA_AVAIL) {  /* Retry? */
+               debug("Error left over data\n");
+               return -EIO;
+       }
+
+       return size;
+}
+
+static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       int rc, status;
+       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 (tpm_tis_i2c_request_locality(dev, 0) < 0)
+               return -EBUSY;
+
+       status = tpm_tis_i2c_status(dev);
+       if ((status & TPM_STS_COMMAND_READY) == 0) {
+               rc = tpm_tis_i2c_ready(dev);
+               if (rc)
+                       return rc;
+               rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
+                                              chip->timeout_b, &status);
+               if (rc)
+                       return rc;
+       }
+
+       burstcnt = tpm_tis_i2c_get_burstcount(dev);
+
+       /* burstcount < 0 -> tpm is busy */
+       if (burstcnt < 0)
+               return burstcnt;
+
+       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_LEN)
+                       burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION_LEN;
+#endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */
+
+               rc = tpm_tis_i2c_write(dev, TPM_DATA_FIFO(chip->locality),
+                                      &(buf[count]), burstcnt);
+               if (rc == 0)
+                       count += burstcnt;
+               else {
+                       debug("%s: error\n", __func__);
+                       if (retry++ > 10)
+                               return -EIO;
+                       rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID,
+                                                      chip->timeout_c,
+                                                      &status);
+                       if (rc)
+                               return rc;
+
+                       if ((status & TPM_STS_DATA_EXPECT) == 0)
+                               return -EIO;
+               }
+       }
+
+       /* Go and do it */
+       rc = tpm_tis_i2c_write(dev, TPM_STS(chip->locality), &sts, 1);
+       if (rc < 0)
+               return rc;
+       debug("%s: done, rc=%d\n", __func__, rc);
+
+       return len;
+}
+
+static int tpm_tis_i2c_cleanup(struct udevice *dev)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+
+       tpm_tis_i2c_ready(dev);
+       /*
+        * The TPM needs some time to clean up here,
+        * so we sleep rather than keeping the bus busy
+        */
+       mdelay(2);
+       tpm_tis_i2c_release_locality(dev, chip->locality, 0);
+
+       return 0;
+}
+
+static int tpm_tis_i2c_init(struct udevice *dev)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       u32 vendor;
+       u32 expected_did_vid;
+       int rc;
+
+       chip->is_open = 1;
+
+       /* Default timeouts - these could move to the device tree */
+       chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
+       chip->timeout_b = TIS_LONG_TIMEOUT_MS;
+       chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
+       chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
+
+       rc = tpm_tis_i2c_request_locality(dev, 0);
+       if (rc < 0)
+               return rc;
+
+       /* Read four bytes from DID_VID register */
+       if (tpm_tis_i2c_read(dev, TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
+               tpm_tis_i2c_release_locality(dev, 0, 1);
+               return -EIO;
+       }
+
+       if (chip->chip_type == SLB9635) {
+               vendor = be32_to_cpu(vendor);
+               expected_did_vid = TPM_TIS_I2C_DID_VID_9635;
+       } else {
+               /* device id and byte order has changed for newer i2c tpms */
+               expected_did_vid = TPM_TIS_I2C_DID_VID_9645;
+       }
+
+       if (chip->chip_type != UNKNOWN && vendor != expected_did_vid) {
+               error("Vendor id did not match! ID was %08x\n", vendor);
+               return -ENODEV;
+       }
+
+       chip->vend_dev = vendor;
+       debug("1.2 TPM (chip type %s device-id 0x%X)\n",
+             chip_name[chip->chip_type], vendor >> 16);
+
+       /*
+        * A timeout query to TPM can be placed here.
+        * Standard timeout values are used so far
+        */
+
+       return 0;
+}
+
+static int tpm_tis_i2c_open(struct udevice *dev)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+       int rc;
+
+       debug("%s: start\n", __func__);
+       if (chip->is_open)
+               return -EBUSY;
+       rc = tpm_tis_i2c_init(dev);
+       if (rc < 0)
+               chip->is_open = 0;
+
+       return rc;
+}
+
+static int tpm_tis_i2c_close(struct udevice *dev)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+
+       if (chip->is_open) {
+               tpm_tis_i2c_release_locality(dev, chip->locality, 1);
+               chip->is_open = 0;
+               chip->vend_dev = 0;
+       }
+
+       return 0;
+}
+
+static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
+{
+       struct tpm_chip *chip = dev_get_priv(dev);
+
+       if (size < 50)
+               return -ENOSPC;
+
+       return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)",
+                       chip->is_open ? "open" : "closed",
+                       chip_name[chip->chip_type],
+                       chip->vend_dev >> 16);
+}
+
+static int tpm_tis_i2c_probe(struct udevice *dev)
+{
+       struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
+       struct tpm_chip *chip = dev_get_priv(dev);
+
+       chip->chip_type = dev_get_driver_data(dev);
+
+       /* TODO: These need to be checked and tuned */
+       uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
+       uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
+       uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
+       uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
+
+       return 0;
+}
+
+static const struct tpm_ops tpm_tis_i2c_ops = {
+       .open           = tpm_tis_i2c_open,
+       .close          = tpm_tis_i2c_close,
+       .get_desc       = tpm_tis_get_desc,
+       .send           = tpm_tis_i2c_send,
+       .recv           = tpm_tis_i2c_recv,
+       .cleanup        = tpm_tis_i2c_cleanup,
+};
+
+static const struct udevice_id tpm_tis_i2c_ids[] = {
+       { .compatible = "infineon,slb9635tt", .data = SLB9635 },
+       { .compatible = "infineon,slb9645tt", .data = SLB9645 },
+       { }
+};
+
+U_BOOT_DRIVER(tpm_tis_i2c) = {
+       .name   = "tpm_tis_infineon",
+       .id     = UCLASS_TPM,
+       .of_match = tpm_tis_i2c_ids,
+       .ops    = &tpm_tis_i2c_ops,
+       .probe  = tpm_tis_i2c_probe,
+       .priv_auto_alloc_size = sizeof(struct tpm_chip),
+};
diff --git a/drivers/tpm/tpm_tis_infineon.h b/drivers/tpm/tpm_tis_infineon.h
new file mode 100644 (file)
index 0000000..3b510d1
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <huewe.external@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.
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ */
+
+#ifndef _TPM_TIS_I2C_H
+#define _TPM_TIS_I2C_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+enum tpm_timeout {
+       TPM_TIMEOUT_MS                  = 5,
+       TIS_SHORT_TIMEOUT_MS            = 750,
+       TIS_LONG_TIMEOUT_MS             = 2000,
+       SLEEP_DURATION_US               = 60,
+       SLEEP_DURATION_LONG_US          = 210,
+};
+
+/* Size of external transmit buffer (used in tpm_transmit)*/
+#define TPM_BUFSIZE 4096
+
+/* Index of Count field in TPM response buffer */
+#define TPM_RSP_SIZE_BYTE      2
+#define TPM_RSP_RC_BYTE                6
+
+enum i2c_chip_type {
+       SLB9635,
+       SLB9645,
+       UNKNOWN,
+};
+
+struct tpm_chip {
+       int is_open;
+       int locality;
+       u32 vend_dev;
+       unsigned long timeout_a, timeout_b, timeout_c, timeout_d;  /* msec */
+       enum i2c_chip_type chip_type;
+};
+
+struct tpm_input_header {
+       __be16 tag;
+       __be32 length;
+       __be32 ordinal;
+} __packed;
+
+struct tpm_output_header {
+       __be16 tag;
+       __be32 length;
+       __be32 return_code;
+} __packed;
+
+struct timeout_t {
+       __be32 a;
+       __be32 b;
+       __be32 c;
+       __be32 d;
+} __packed;
+
+struct duration_t {
+       __be32 tpm_short;
+       __be32 tpm_medium;
+       __be32 tpm_long;
+} __packed;
+
+union cap_t {
+       struct timeout_t timeout;
+       struct duration_t duration;
+};
+
+struct tpm_getcap_params_in {
+       __be32 cap;
+       __be32 subcap_size;
+       __be32 subcap;
+} __packed;
+
+struct tpm_getcap_params_out {
+       __be32 cap_size;
+       union cap_t cap;
+} __packed;
+
+union tpm_cmd_header {
+       struct tpm_input_header in;
+       struct tpm_output_header out;
+};
+
+union tpm_cmd_params {
+       struct tpm_getcap_params_out getcap_out;
+       struct tpm_getcap_params_in getcap_in;
+};
+
+struct tpm_cmd_t {
+       union tpm_cmd_header header;
+       union tpm_cmd_params params;
+} __packed;
+
+/* Max number of iterations after i2c NAK */
+#define MAX_COUNT              3
+
+/*
+ * 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
+
+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,
+};
+
+/* expected value for DIDVID register */
+#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
+#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
+
+#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))
+
+#endif