630 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			630 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * 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 <config.h>
 | 
						|
#include <common.h>
 | 
						|
#include <compiler.h>
 | 
						|
#include <fdtdec.h>
 | 
						|
#include <i2c.h>
 | 
						|
#include <tpm.h>
 | 
						|
#include <asm-generic/errno.h>
 | 
						|
#include <linux/types.h>
 | 
						|
#include <linux/unaligned/be_byteshift.h>
 | 
						|
 | 
						|
#include "tpm_private.h"
 | 
						|
 | 
						|
DECLARE_GLOBAL_DATA_PTR;
 | 
						|
 | 
						|
/* TPM configuration */
 | 
						|
struct tpm {
 | 
						|
	int i2c_bus;
 | 
						|
	int slave_addr;
 | 
						|
	char inited;
 | 
						|
	int old_bus;
 | 
						|
} tpm;
 | 
						|
 | 
						|
/* Global structure for tpm chip data */
 | 
						|
static struct tpm_chip g_chip;
 | 
						|
 | 
						|
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,
 | 
						|
};
 | 
						|
 | 
						|
/* 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];
 | 
						|
	}
 | 
						|
 | 
						|
	if (duration_idx != TPM_UNDEFINED)
 | 
						|
		duration = chip->vendor.duration[duration_idx];
 | 
						|
 | 
						|
	if (duration <= 0)
 | 
						|
		return 2 * 60 * HZ; /* Two minutes timeout */
 | 
						|
	else
 | 
						|
		return duration;
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz)
 | 
						|
{
 | 
						|
	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) {
 | 
						|
		error("no data\n");
 | 
						|
		return -ENODATA;
 | 
						|
	}
 | 
						|
	if (count > bufsiz) {
 | 
						|
		error("invalid count value %x %zx\n", count, bufsiz);
 | 
						|
		return -E2BIG;
 | 
						|
	}
 | 
						|
 | 
						|
	rc = chip->vendor.send(chip, (u8 *)buf, count);
 | 
						|
	if (rc < 0) {
 | 
						|
		error("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 {
 | 
						|
		debug("waiting for status...\n");
 | 
						|
		u8 status = chip->vendor.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);
 | 
						|
 | 
						|
	chip->vendor.cancel(chip);
 | 
						|
	error("Operation Timed out\n");
 | 
						|
	rc = -ETIME;
 | 
						|
	goto out;
 | 
						|
 | 
						|
out_recv:
 | 
						|
	debug("out_recv: reading response...\n");
 | 
						|
	rc = chip->vendor.recv(chip, (u8 *)buf, TPM_BUFSIZE);
 | 
						|
	if (rc < 0)
 | 
						|
		error("tpm_transmit: tpm_recv: error %zd\n", rc);
 | 
						|
 | 
						|
out:
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static 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;
 | 
						|
}
 | 
						|
 | 
						|
static void tpm_close(void)
 | 
						|
{
 | 
						|
	if (g_chip.is_open) {
 | 
						|
		tpm_vendor_cleanup(&g_chip);
 | 
						|
		g_chip.is_open = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int tpm_select(void)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	tpm.old_bus = i2c_get_bus_num();
 | 
						|
	if (tpm.old_bus != tpm.i2c_bus) {
 | 
						|
		ret = i2c_set_bus_num(tpm.i2c_bus);
 | 
						|
		if (ret) {
 | 
						|
			debug("%s: Fail to set i2c bus %d\n", __func__,
 | 
						|
			      tpm.i2c_bus);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int tpm_deselect(void)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (tpm.old_bus != i2c_get_bus_num()) {
 | 
						|
		ret = i2c_set_bus_num(tpm.old_bus);
 | 
						|
		if (ret) {
 | 
						|
			debug("%s: Fail to restore i2c bus %d\n",
 | 
						|
			      __func__, tpm.old_bus);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	tpm.old_bus = -1;
 | 
						|
	return 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)
 | 
						|
{
 | 
						|
#ifdef CONFIG_OF_CONTROL
 | 
						|
	const void *blob = gd->fdt_blob;
 | 
						|
	int node, parent;
 | 
						|
	int i2c_bus;
 | 
						|
 | 
						|
	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;
 | 
						|
	}
 | 
						|
	i2c_bus = i2c_get_bus_num_fdt(parent);
 | 
						|
	if (i2c_bus < 0)
 | 
						|
		return -1;
 | 
						|
	dev->i2c_bus = i2c_bus;
 | 
						|
	dev->slave_addr = fdtdec_get_addr(blob, node, "reg");
 | 
						|
#else
 | 
						|
	dev->i2c_bus = CONFIG_TPM_TIS_I2C_BUS_NUMBER;
 | 
						|
	dev->slave_addr = CONFIG_TPM_TIS_I2C_SLAVE_ADDRESS;
 | 
						|
#endif
 | 
						|
	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;
 | 
						|
 | 
						|
	if (tpm_select())
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Probe TPM twice; the first probing might fail because TPM is asleep,
 | 
						|
	 * and the probing can wake up TPM.
 | 
						|
	 */
 | 
						|
	if (i2c_probe(tpm.slave_addr) && i2c_probe(tpm.slave_addr)) {
 | 
						|
		debug("%s: fail to probe i2c addr 0x%x\n", __func__,
 | 
						|
		      tpm.slave_addr);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	tpm_deselect();
 | 
						|
 | 
						|
	tpm.inited = 1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int tis_open(void)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
 | 
						|
	if (!tpm.inited)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (tpm_select())
 | 
						|
		return -1;
 | 
						|
 | 
						|
	rc = tpm_open(tpm.slave_addr);
 | 
						|
 | 
						|
	tpm_deselect();
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
int tis_close(void)
 | 
						|
{
 | 
						|
	if (!tpm.inited)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (tpm_select())
 | 
						|
		return -1;
 | 
						|
 | 
						|
	tpm_close();
 | 
						|
 | 
						|
	tpm_deselect();
 | 
						|
 | 
						|
	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);
 | 
						|
 | 
						|
	if (tpm_select())
 | 
						|
		return -1;
 | 
						|
 | 
						|
	len = tpm_transmit(buf, sbuf_size);
 | 
						|
 | 
						|
	tpm_deselect();
 | 
						|
 | 
						|
	if (len < 10) {
 | 
						|
		*rbuf_len = 0;
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	memcpy(recvbuf, buf, len);
 | 
						|
	*rbuf_len = len;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |