summaryrefslogtreecommitdiff
path: root/drivers/tpm/slb9635_i2c
diff options
context:
space:
mode:
authorTom Wai-Hong Tam <waihong@chromium.org>2013-04-12 11:04:37 +0000
committerSimon Glass <sjg@chromium.org>2013-06-03 01:31:23 -0700
commit1b393db5870927d68c42a46e6c5877c8d0d83910 (patch)
tree9c8357f65d5221778bcd7a9272c40823eeb89649 /drivers/tpm/slb9635_i2c
parentec34fa5e43bfbef5a93394db04ff3b03fdfe3f44 (diff)
downloadu-boot-imx-1b393db5870927d68c42a46e6c5877c8d0d83910.zip
u-boot-imx-1b393db5870927d68c42a46e6c5877c8d0d83910.tar.gz
u-boot-imx-1b393db5870927d68c42a46e6c5877c8d0d83910.tar.bz2
tpm: Reorganize the I2C TPM driver
This patch does a similar code reogranzation from http://patchwork.ozlabs.org/patch/132179/ which is based on an old version of code (fdt support and bus selection still not in). It merges this tidy-up on top of the recent code. It does not make any logical change. tpm.c implements the interface defined in tpm.h based on underlying LPC or I2C TPM driver. tpm.c and the underlying driver communicate throught tpm_private.h. Note: Merging the LPC driver with tpm.c is left to future patches. Change-Id: Ie1384f5f9e3935d3bc9a44adf8de80c5a70a5f2b Signed-off-by: Tom Wai-Hong Tam <waihong@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/tpm/slb9635_i2c')
-rw-r--r--drivers/tpm/slb9635_i2c/compatibility.h51
-rw-r--r--drivers/tpm/slb9635_i2c/tpm.c453
-rw-r--r--drivers/tpm/slb9635_i2c/tpm.h161
-rw-r--r--drivers/tpm/slb9635_i2c/tpm_tis_i2c.c624
4 files changed, 0 insertions, 1289 deletions
diff --git a/drivers/tpm/slb9635_i2c/compatibility.h b/drivers/tpm/slb9635_i2c/compatibility.h
deleted file mode 100644
index 62dc9fa..0000000
--- a/drivers/tpm/slb9635_i2c/compatibility.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2011 Infineon Technologies
- *
- * Authors:
- * Peter Huewe <huewe.external@infineon.com>
- *
- * 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; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#ifndef _COMPATIBILITY_H_
-#define _COMPATIBILITY_H_
-
-/* all includes from U-Boot */
-#include <linux/types.h>
-#include <linux/unaligned/be_byteshift.h>
-#include <asm-generic/errno.h>
-#include <compiler.h>
-#include <common.h>
-
-/* extended error numbers from linux (see errno.h) */
-#define ECANCELED 125 /* Operation Canceled */
-
-#define msleep(t) udelay((t)*1000)
-
-/* Timer frequency. Corresponds to msec timer resolution*/
-#define HZ 1000
-
-#define dev_dbg(dev, format, arg...) debug(format, ##arg)
-#define dev_err(dev, format, arg...) printf(format, ##arg)
-#define dev_info(dev, format, arg...) debug(format, ##arg)
-#define dbg_printf debug
-
-#endif
diff --git a/drivers/tpm/slb9635_i2c/tpm.c b/drivers/tpm/slb9635_i2c/tpm.c
deleted file mode 100644
index 496c48e..0000000
--- a/drivers/tpm/slb9635_i2c/tpm.c
+++ /dev/null
@@ -1,453 +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
- *
- * It is based on the Linux kernel driver tpm.c from Leendert van
- * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
- *
- * Version: 2.1.1
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <malloc.h>
-#include "tpm.h"
-
-/* global structure for tpm chip data */
-struct tpm_chip g_chip;
-
-enum tpm_duration {
- TPM_SHORT = 0,
- TPM_MEDIUM = 1,
- TPM_LONG = 2,
- TPM_UNDEFINED,
-};
-
-#define TPM_MAX_ORDINAL 243
-#define TPM_MAX_PROTECTED_ORDINAL 12
-#define TPM_PROTECTED_ORDINAL_MASK 0xFF
-
-/*
- * 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,
-};
-
-/*
- * Returns max number of milliseconds to wait
- */
-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];
-
- if (duration_idx != TPM_UNDEFINED)
- duration = chip->vendor.duration[duration_idx];
- if (duration <= 0)
- return 2 * 60 * HZ; /*two minutes timeout*/
- else
- return duration;
-}
-
-#define TPM_CMD_COUNT_BYTE 2
-#define TPM_CMD_ORDINAL_BYTE 6
-
-ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
-{
- ssize_t 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) {
- dev_err(chip->dev, "no data\n");
- return -ENODATA;
- }
- if (count > bufsiz) {
- dev_err(chip->dev,
- "invalid count value %x %zx\n", count, bufsiz);
- return -E2BIG;
- }
-
- rc = chip->vendor.send(chip, (u8 *)buf, count);
- if (rc < 0) {
- dev_err(chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc);
- goto out;
- }
-
- if (chip->vendor.irq)
- goto out_recv;
-
- start = get_timer(0);
- stop = tpm_calc_ordinal_duration(chip, ordinal);
- do {
- dbg_printf("waiting for status...\n");
- u8 status = chip->vendor.status(chip);
- if ((status & chip->vendor.req_complete_mask) ==
- chip->vendor.req_complete_val) {
- dbg_printf("...got it;\n");
- goto out_recv;
- }
-
- if ((status == chip->vendor.req_canceled)) {
- dev_err(chip->dev, "Operation Canceled\n");
- rc = -ECANCELED;
- goto out;
- }
- msleep(TPM_TIMEOUT);
- } while (get_timer(start) < stop);
-
- chip->vendor.cancel(chip);
- dev_err(chip->dev, "Operation Timed out\n");
- rc = -ETIME;
- goto out;
-
-out_recv:
-
- dbg_printf("out_recv: reading response...\n");
- rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE);
- if (rc < 0)
- dev_err(chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc);
-out:
- return rc;
-}
-
-#define TPM_ERROR_SIZE 10
-
-enum tpm_capabilities {
- TPM_CAP_PROP = cpu_to_be32(5),
-};
-
-enum tpm_sub_capabilities {
- TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
- TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
-};
-
-struct tpm_chip *tpm_register_hardware(const struct tpm_vendor_specific *entry)
-{
- struct tpm_chip *chip;
-
- /* Driver specific per-device data */
- chip = &g_chip;
- memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific));
- chip->is_open = 1;
-
- return chip;
-}
-
-int tpm_open(uint32_t dev_addr)
-{
- int rc;
- if (g_chip.is_open)
- return -EBUSY;
- rc = tpm_vendor_init(dev_addr);
- if (rc < 0)
- g_chip.is_open = 0;
- return rc;
-}
-
-void tpm_close(void)
-{
- if (g_chip.is_open) {
- tpm_vendor_cleanup(&g_chip);
- g_chip.is_open = 0;
- }
-}
diff --git a/drivers/tpm/slb9635_i2c/tpm.h b/drivers/tpm/slb9635_i2c/tpm.h
deleted file mode 100644
index 9ddee86..0000000
--- a/drivers/tpm/slb9635_i2c/tpm.h
+++ /dev/null
@@ -1,161 +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.
- *
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#ifndef _TPM_H_
-#define _TPM_H_
-
-#include <linux/compiler.h>
-
-#include "compatibility.h"
-
-enum tpm_timeout {
- TPM_TIMEOUT = 5, /* msecs */
-};
-
-/* Size of external transmit buffer (used in tpm_transmit)*/
-#define TPM_BUFSIZE 4096
-
-/* Index of fields in TPM command buffer */
-#define TPM_CMD_SIZE_BYTE 2
-#define TPM_CMD_ORDINAL_BYTE 6
-
-/* Index of Count field in TPM response buffer */
-#define TPM_RSP_SIZE_BYTE 2
-#define TPM_RSP_RC_BYTE 6
-
-struct tpm_chip;
-
-struct tpm_vendor_specific {
- const u8 req_complete_mask;
- const u8 req_complete_val;
- const u8 req_canceled;
- int irq;
- int (*recv) (struct tpm_chip *, u8 *, size_t);
- int (*send) (struct tpm_chip *, u8 *, size_t);
- void (*cancel) (struct tpm_chip *);
- u8(*status) (struct tpm_chip *);
- int locality;
- unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */
- unsigned long duration[3]; /* msec */
-};
-
-struct tpm_chip {
- int is_open;
- struct tpm_vendor_specific vendor;
-};
-
-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;
-
-
-/* ---------- Interface for TPM vendor ------------ */
-
-extern struct tpm_chip *tpm_register_hardware(
- const struct tpm_vendor_specific *);
-
-extern int tpm_vendor_init(uint32_t dev_addr);
-
-extern void tpm_vendor_cleanup(struct tpm_chip *chip);
-
-/* ---------- Interface for TDDL ------------------- */
-
-/*
- * if dev_addr != 0 - redefines TPM device address
- * Returns < 0 on error, 0 on success.
- */
-extern int tpm_open(uint32_t dev_addr);
-
-extern void tpm_close(void);
-
-/*
- * Transmit bufsiz bytes out of buf to TPM and get results back in buf, too.
- * Returns < 0 on error, 0 on success.
- */
-extern ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz);
-
-#endif
diff --git a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c b/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c
deleted file mode 100644
index c2e1041..0000000
--- a/drivers/tpm/slb9635_i2c/tpm_tis_i2c.c
+++ /dev/null
@@ -1,624 +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
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <common.h>
-#include <fdtdec.h>
-#include <i2c.h>
-#include <linux/types.h>
-
-#include "compatibility.h"
-#include "tpm.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-/* max. buffer size supported by our tpm */
-#ifdef TPM_BUFSIZE
-#undef TPM_BUFSIZE
-#endif
-#define TPM_BUFSIZE 1260
-/* Address of the TPM on the I2C bus */
-#define TPM_I2C_ADDR 0x20
-/* max. number of iterations after i2c NAK */
-#define MAX_COUNT 3
-
-#define SLEEP_DURATION 60 /*in usec*/
-
-/* max. number of iterations after i2c NAK for 'long' commands
- * we need this especially for sending TPM_READY, since the cleanup after the
- * transtion to the ready state may take some time, but it is unpredictable
- * how long it will take.
- */
-#define MAX_COUNT_LONG 50
-
-#define SLEEP_DURATION_LONG 210 /* in usec */
-
-/* expected value for DIDVID register */
-#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
-#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
-
-enum i2c_chip_type {
- SLB9635,
- SLB9645,
- UNKNOWN,
-};
-
-static const char * const chip_name[] = {
- [SLB9635] = "slb9635tt",
- [SLB9645] = "slb9645tt",
- [UNKNOWN] = "unknown/fallback to slb9635",
-};
-
-/* Structure to store I2C TPM specific stuff */
-struct tpm_inf_dev {
- uint addr;
- u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
- enum i2c_chip_type chip_type;
-};
-
-static struct tpm_inf_dev tpm_dev = {
- .addr = TPM_I2C_ADDR
-};
-
-/*
- * iic_tpm_read() - read from TPM register
- * @addr: register address to read from
- * @buffer: provided by caller
- * @len: number of bytes to read
- *
- * Read len bytes from TPM register and put them into
- * buffer (little-endian format, i.e. first byte is put into buffer[0]).
- *
- * NOTE: TPM is big-endian for multi-byte values. Multi-byte
- * values have to be swapped.
- *
- * Return -EIO on error, 0 on success.
- */
-int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
-{
- int rc;
- int count;
- uint myaddr = addr;
- /* we have to use uint here, uchar hangs the board */
-
- 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 *)&myaddr, 1);
- if (rc == 0)
- break; /* success, break to skip sleep */
-
- udelay(SLEEP_DURATION);
- }
-
- if (rc)
- return -rc;
-
- /* After the TPM has successfully received the register address
- * it needs some time, thus we're sleeping here again, before
- * retrieving the data
- */
- for (count = 0; count < MAX_COUNT; count++) {
- udelay(SLEEP_DURATION);
- rc = i2c_read(tpm_dev.addr, 0, 0, buffer, len);
- if (rc == 0)
- break; /* success, break to skip sleep */
- }
- } else {
- /* use a combined read for newer chips
- * unfortunately the smbus functions are not suitable due to
- * the 32 byte limit of the smbus.
- * retries should usually not be needed, but are kept just to
- * be safe on the safe side.
- */
- for (count = 0; count < MAX_COUNT; count++) {
- rc = i2c_read(tpm_dev.addr, addr, 1, buffer, len);
- if (rc == 0)
- break; /* break here to skip sleep */
- udelay(SLEEP_DURATION);
- }
- }
-
- /* take care of 'guard time' */
- udelay(SLEEP_DURATION);
- if (rc)
- return -rc;
-
- return 0;
-}
-
-static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
- unsigned int sleep_time,
- u8 max_count)
-{
- 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);
- if (rc == 0)
- break; /* success, break to skip sleep */
-
- udelay(sleep_time);
- }
-
- /* take care of 'guard time' */
- udelay(SLEEP_DURATION);
- if (rc)
- return -rc;
-
- return 0;
-}
-
-/*
- * iic_tpm_write() - write to TPM register
- * @addr: register address to write to
- * @buffer: containing data to be written
- * @len: number of bytes to write
- *
- * Write len bytes from provided buffer to TPM register (little
- * endian format, i.e. buffer[0] is written as first byte).
- *
- * NOTE: TPM is big-endian for multi-byte values. Multi-byte
- * values have to be swapped.
- *
- * NOTE: use this function instead of the iic_tpm_write_generic function.
- *
- * Return -EIO on error, 0 on success
- */
-static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
-{
- return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION,
- MAX_COUNT);
-}
-
-/*
- * This function is needed especially for the cleanup situation after
- * sending TPM_READY
- * */
-static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
-{
- return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG,
- MAX_COUNT_LONG);
-}
-
-#define TPM_HEADER_SIZE 10
-
-enum tis_access {
- TPM_ACCESS_VALID = 0x80,
- TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
- TPM_ACCESS_REQUEST_PENDING = 0x04,
- TPM_ACCESS_REQUEST_USE = 0x02,
-};
-
-enum tis_status {
- TPM_STS_VALID = 0x80,
- TPM_STS_COMMAND_READY = 0x40,
- TPM_STS_GO = 0x20,
- TPM_STS_DATA_AVAIL = 0x10,
- TPM_STS_DATA_EXPECT = 0x08,
-};
-
-enum tis_defaults {
- TIS_SHORT_TIMEOUT = 750, /* ms */
- TIS_LONG_TIMEOUT = 2000, /* 2 sec */
-};
-
-#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
-#define TPM_STS(l) (0x0001 | ((l) << 4))
-#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
-#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
-
-static int check_locality(struct tpm_chip *chip, int loc)
-{
- u8 buf;
- int rc;
-
- rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
- if (rc < 0)
- return rc;
-
- if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
- (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
- chip->vendor.locality = loc;
- return loc;
- }
-
- return -1;
-}
-
-static void release_locality(struct tpm_chip *chip, int loc, int force)
-{
- u8 buf;
- if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
- return;
-
- if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
- (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
- buf = TPM_ACCESS_ACTIVE_LOCALITY;
- iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
- }
-}
-
-static int request_locality(struct tpm_chip *chip, int loc)
-{
- unsigned long start, stop;
- u8 buf = TPM_ACCESS_REQUEST_USE;
-
- if (check_locality(chip, loc) >= 0)
- return loc; /* we already have the locality */
-
- iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
-
- /* wait for burstcount */
- start = get_timer(0);
- stop = chip->vendor.timeout_a;
- do {
- if (check_locality(chip, loc) >= 0)
- return loc;
- msleep(TPM_TIMEOUT);
- } while (get_timer(start) < stop);
-
- return -1;
-}
-
-static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
-{
- /* 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)
- return 0;
- else
- return buf;
-}
-
-static void tpm_tis_i2c_ready(struct tpm_chip *chip)
-{
- /* 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);
-}
-
-static ssize_t get_burstcount(struct tpm_chip *chip)
-{
- unsigned long start, stop;
- ssize_t burstcnt;
- u8 buf[3];
-
- /* wait for burstcount */
- /* which timeout value, spec has 2 answers (c & d) */
- start = get_timer(0);
- stop = chip->vendor.timeout_d;
- do {
- /* Note: STS is little endian */
- if (iic_tpm_read(TPM_STS(chip->vendor.locality) + 1, buf, 3)
- < 0)
- burstcnt = 0;
- else
- burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
-
- if (burstcnt)
- return burstcnt;
- msleep(TPM_TIMEOUT);
- } while (get_timer(start) < stop);
-
- return -EBUSY;
-}
-
-static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
- int *status)
-{
- unsigned long start, stop;
-
- /* check current status */
- *status = tpm_tis_i2c_status(chip);
- if ((*status & mask) == mask)
- return 0;
-
- start = get_timer(0);
- stop = timeout;
- do {
- msleep(TPM_TIMEOUT);
- *status = tpm_tis_i2c_status(chip);
- if ((*status & mask) == mask)
- return 0;
-
- } while (get_timer(start) < stop);
-
- return -ETIME;
-}
-
-static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
-{
- size_t size = 0;
- ssize_t burstcnt;
- int rc;
-
- while (size < count) {
- burstcnt = get_burstcount(chip);
-
- /* burstcount < 0 = tpm is busy */
- if (burstcnt < 0)
- return burstcnt;
-
- /* limit received data to max. left */
- if (burstcnt > (count - size))
- burstcnt = count - size;
-
- rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
- &(buf[size]),
- burstcnt);
- if (rc == 0)
- size += burstcnt;
- }
-
- return size;
-}
-
-static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
-{
- int size = 0;
- int expected, status;
-
- if (count < TPM_HEADER_SIZE) {
- size = -EIO;
- goto out;
- }
-
- /* read first 10 bytes, including tag, paramsize, and result */
- size = recv_data(chip, buf, TPM_HEADER_SIZE);
- if (size < TPM_HEADER_SIZE) {
- dev_err(chip->dev, "Unable to read header\n");
- goto out;
- }
-
- expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
- if ((size_t)expected > count) {
- size = -EIO;
- goto out;
- }
-
- size += recv_data(chip, &buf[TPM_HEADER_SIZE],
- expected - TPM_HEADER_SIZE);
- if (size < expected) {
- dev_err(chip->dev, "Unable to read remainder of result\n");
- size = -ETIME;
- goto out;
- }
-
- wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
- if (status & TPM_STS_DATA_AVAIL) { /* retry? */
- dev_err(chip->dev, "Error left over data\n");
- size = -EIO;
- goto out;
- }
-
-out:
- 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);
-
- 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 count = 0;
- u8 sts = TPM_STS_GO;
-
- if (len > TPM_BUFSIZE)
- return -E2BIG; /* command is too long for our tpm, sorry */
-
- if (request_locality(chip, 0) < 0)
- return -EBUSY;
-
- status = tpm_tis_i2c_status(chip);
- if ((status & TPM_STS_COMMAND_READY) == 0) {
- tpm_tis_i2c_ready(chip);
- if (wait_for_stat
- (chip, TPM_STS_COMMAND_READY,
- chip->vendor.timeout_b, &status) < 0) {
- rc = -ETIME;
- goto out_err;
- }
- }
-
- while (count < len - 1) {
- burstcnt = get_burstcount(chip);
-
- /* burstcount < 0 = tpm is busy */
- if (burstcnt < 0)
- return burstcnt;
-
- if (burstcnt > (len-1-count))
- burstcnt = len-1-count;
-
-#ifdef CONFIG_TPM_I2C_BURST_LIMITATION
- if (burstcnt > CONFIG_TPM_I2C_BURST_LIMITATION)
- burstcnt = CONFIG_TPM_I2C_BURST_LIMITATION;
-#endif /* CONFIG_TPM_I2C_BURST_LIMITATION */
-
- rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
- &(buf[count]), burstcnt);
- if (rc == 0)
- count += burstcnt;
-
- wait_for_stat(chip, TPM_STS_VALID,
- chip->vendor.timeout_c, &status);
-
- if ((status & TPM_STS_DATA_EXPECT) == 0) {
- rc = -EIO;
- goto out_err;
- }
- }
-
- /* write last byte */
- iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
- wait_for_stat(chip, TPM_STS_VALID, 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);
-
- return len;
-out_err:
- 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);
-
- 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)
-{
-#ifdef CONFIG_OF_CONTROL
- const void *blob = gd->fdt_blob;
-
- if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9645_TPM) >= 0)
- return SLB9645;
-
- if (fdtdec_next_compatible(blob, 0, COMPAT_INFINEON_SLB9635_TPM) >= 0)
- return SLB9635;
-#endif
- return UNKNOWN;
-}
-
-/* initialisation of i2c tpm */
-
-
-int tpm_vendor_init(uint32_t dev_addr)
-{
- 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;
- }
-
- /* Disable interrupts (not supported) */
- chip->vendor.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;
- }
-
- /* read four bytes from DID_VID register */
- if (iic_tpm_read(TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
- rc = -EIO;
- goto out_release;
- }
-
- if (tpm_dev.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 (tpm_dev.chip_type != UNKNOWN && vendor != expected_did_vid) {
- dev_err(dev, "vendor id did not match! ID was %08x\n", vendor);
- rc = -ENODEV;
- goto out_release;
- }
-
- dev_info(dev, "1.2 TPM (chip type %s device-id 0x%X)\n",
- chip_name[tpm_dev.chip_type], vendor >> 16);
-
- /*
- * A timeout query to TPM can be placed here.
- * Standard timeout values are used so far
- */
-
- return 0;
-
-out_release:
- release_locality(chip, 0, 1);
-
-out_err:
- tpm_dev.addr = old_addr;
- return rc;
-}
-
-void tpm_vendor_cleanup(struct tpm_chip *chip)
-{
- release_locality(chip, chip->vendor.locality, 1);
-}