xref: /openbmc/u-boot/drivers/tpm/tpm_tis_lpc.c (revision 1a459660)
190899cc0SChe-liang Chiou /*
290899cc0SChe-liang Chiou  * Copyright (c) 2011 The Chromium OS Authors.
390899cc0SChe-liang Chiou  *
4*1a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
590899cc0SChe-liang Chiou  */
690899cc0SChe-liang Chiou 
790899cc0SChe-liang Chiou /*
890899cc0SChe-liang Chiou  * The code in this file is based on the article "Writing a TPM Device Driver"
990899cc0SChe-liang Chiou  * published on http://ptgmedia.pearsoncmg.com.
1090899cc0SChe-liang Chiou  *
1190899cc0SChe-liang Chiou  * One principal difference is that in the simplest config the other than 0
1290899cc0SChe-liang Chiou  * TPM localities do not get mapped by some devices (for instance, by Infineon
1390899cc0SChe-liang Chiou  * slb9635), so this driver provides access to locality 0 only.
1490899cc0SChe-liang Chiou  */
1590899cc0SChe-liang Chiou 
1690899cc0SChe-liang Chiou #include <common.h>
1790899cc0SChe-liang Chiou #include <asm/io.h>
1890899cc0SChe-liang Chiou #include <tpm.h>
1990899cc0SChe-liang Chiou 
2090899cc0SChe-liang Chiou #define PREFIX "lpc_tpm: "
2190899cc0SChe-liang Chiou 
2290899cc0SChe-liang Chiou struct tpm_locality {
2390899cc0SChe-liang Chiou 	u32 access;
2490899cc0SChe-liang Chiou 	u8 padding0[4];
2590899cc0SChe-liang Chiou 	u32 int_enable;
2690899cc0SChe-liang Chiou 	u8 vector;
2790899cc0SChe-liang Chiou 	u8 padding1[3];
2890899cc0SChe-liang Chiou 	u32 int_status;
2990899cc0SChe-liang Chiou 	u32 int_capability;
3090899cc0SChe-liang Chiou 	u32 tpm_status;
3190899cc0SChe-liang Chiou 	u8 padding2[8];
3290899cc0SChe-liang Chiou 	u8 data;
3390899cc0SChe-liang Chiou 	u8 padding3[3803];
3490899cc0SChe-liang Chiou 	u32 did_vid;
3590899cc0SChe-liang Chiou 	u8 rid;
3690899cc0SChe-liang Chiou 	u8 padding4[251];
3790899cc0SChe-liang Chiou };
3890899cc0SChe-liang Chiou 
3990899cc0SChe-liang Chiou /*
4090899cc0SChe-liang Chiou  * This pointer refers to the TPM chip, 5 of its localities are mapped as an
4190899cc0SChe-liang Chiou  * array.
4290899cc0SChe-liang Chiou  */
4390899cc0SChe-liang Chiou #define TPM_TOTAL_LOCALITIES	5
4490899cc0SChe-liang Chiou static struct tpm_locality *lpc_tpm_dev =
4590899cc0SChe-liang Chiou 	(struct tpm_locality *)CONFIG_TPM_TIS_BASE_ADDRESS;
4690899cc0SChe-liang Chiou 
4790899cc0SChe-liang Chiou /* Some registers' bit field definitions */
4890899cc0SChe-liang Chiou #define TIS_STS_VALID                  (1 << 7) /* 0x80 */
4990899cc0SChe-liang Chiou #define TIS_STS_COMMAND_READY          (1 << 6) /* 0x40 */
5090899cc0SChe-liang Chiou #define TIS_STS_TPM_GO                 (1 << 5) /* 0x20 */
5190899cc0SChe-liang Chiou #define TIS_STS_DATA_AVAILABLE         (1 << 4) /* 0x10 */
5290899cc0SChe-liang Chiou #define TIS_STS_EXPECT                 (1 << 3) /* 0x08 */
5390899cc0SChe-liang Chiou #define TIS_STS_RESPONSE_RETRY         (1 << 1) /* 0x02 */
5490899cc0SChe-liang Chiou 
5590899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_REG_VALID_STS   (1 << 7) /* 0x80 */
5690899cc0SChe-liang Chiou #define TIS_ACCESS_ACTIVE_LOCALITY     (1 << 5) /* 0x20 */
5790899cc0SChe-liang Chiou #define TIS_ACCESS_BEEN_SEIZED         (1 << 4) /* 0x10 */
5890899cc0SChe-liang Chiou #define TIS_ACCESS_SEIZE               (1 << 3) /* 0x08 */
5990899cc0SChe-liang Chiou #define TIS_ACCESS_PENDING_REQUEST     (1 << 2) /* 0x04 */
6090899cc0SChe-liang Chiou #define TIS_ACCESS_REQUEST_USE         (1 << 1) /* 0x02 */
6190899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_ESTABLISHMENT   (1 << 0) /* 0x01 */
6290899cc0SChe-liang Chiou 
6390899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_MASK       (0xffff)
6490899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_SHIFT      (8)
6590899cc0SChe-liang Chiou 
6690899cc0SChe-liang Chiou /*
6790899cc0SChe-liang Chiou  * Error value returned if a tpm register does not enter the expected state
6890899cc0SChe-liang Chiou  * after continuous polling. No actual TPM register reading ever returns -1,
6990899cc0SChe-liang Chiou  * so this value is a safe error indication to be mixed with possible status
7090899cc0SChe-liang Chiou  * register values.
7190899cc0SChe-liang Chiou  */
7290899cc0SChe-liang Chiou #define TPM_TIMEOUT_ERR			(-1)
7390899cc0SChe-liang Chiou 
7490899cc0SChe-liang Chiou /* Error value returned on various TPM driver errors. */
7590899cc0SChe-liang Chiou #define TPM_DRIVER_ERR		(1)
7690899cc0SChe-liang Chiou 
7790899cc0SChe-liang Chiou  /* 1 second is plenty for anything TPM does. */
7890899cc0SChe-liang Chiou #define MAX_DELAY_US	(1000 * 1000)
7990899cc0SChe-liang Chiou 
8090899cc0SChe-liang Chiou /* Retrieve burst count value out of the status register contents. */
8190899cc0SChe-liang Chiou static u16 burst_count(u32 status)
8290899cc0SChe-liang Chiou {
8390899cc0SChe-liang Chiou 	return (status >> TIS_STS_BURST_COUNT_SHIFT) & TIS_STS_BURST_COUNT_MASK;
8490899cc0SChe-liang Chiou }
8590899cc0SChe-liang Chiou 
8690899cc0SChe-liang Chiou /*
8790899cc0SChe-liang Chiou  * Structures defined below allow creating descriptions of TPM vendor/device
8890899cc0SChe-liang Chiou  * ID information for run time discovery. The only device the system knows
8990899cc0SChe-liang Chiou  * about at this time is Infineon slb9635.
9090899cc0SChe-liang Chiou  */
9190899cc0SChe-liang Chiou struct device_name {
9290899cc0SChe-liang Chiou 	u16 dev_id;
9390899cc0SChe-liang Chiou 	const char * const dev_name;
9490899cc0SChe-liang Chiou };
9590899cc0SChe-liang Chiou 
9690899cc0SChe-liang Chiou struct vendor_name {
9790899cc0SChe-liang Chiou 	u16 vendor_id;
9890899cc0SChe-liang Chiou 	const char *vendor_name;
9990899cc0SChe-liang Chiou 	const struct device_name *dev_names;
10090899cc0SChe-liang Chiou };
10190899cc0SChe-liang Chiou 
10290899cc0SChe-liang Chiou static const struct device_name infineon_devices[] = {
10390899cc0SChe-liang Chiou 	{0xb, "SLB9635 TT 1.2"},
10490899cc0SChe-liang Chiou 	{0}
10590899cc0SChe-liang Chiou };
10690899cc0SChe-liang Chiou 
10790899cc0SChe-liang Chiou static const struct vendor_name vendor_names[] = {
10890899cc0SChe-liang Chiou 	{0x15d1, "Infineon", infineon_devices},
10990899cc0SChe-liang Chiou };
11090899cc0SChe-liang Chiou 
11190899cc0SChe-liang Chiou /*
11290899cc0SChe-liang Chiou  * Cached vendor/device ID pair to indicate that the device has been already
11390899cc0SChe-liang Chiou  * discovered.
11490899cc0SChe-liang Chiou  */
11590899cc0SChe-liang Chiou static u32 vendor_dev_id;
11690899cc0SChe-liang Chiou 
11790899cc0SChe-liang Chiou /* TPM access wrappers to support tracing */
11890899cc0SChe-liang Chiou static u8 tpm_read_byte(const u8 *ptr)
11990899cc0SChe-liang Chiou {
12090899cc0SChe-liang Chiou 	u8  ret = readb(ptr);
12190899cc0SChe-liang Chiou 	debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
12290899cc0SChe-liang Chiou 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret);
12390899cc0SChe-liang Chiou 	return ret;
12490899cc0SChe-liang Chiou }
12590899cc0SChe-liang Chiou 
12690899cc0SChe-liang Chiou static u32 tpm_read_word(const u32 *ptr)
12790899cc0SChe-liang Chiou {
12890899cc0SChe-liang Chiou 	u32  ret = readl(ptr);
12990899cc0SChe-liang Chiou 	debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
13090899cc0SChe-liang Chiou 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, ret);
13190899cc0SChe-liang Chiou 	return ret;
13290899cc0SChe-liang Chiou }
13390899cc0SChe-liang Chiou 
13490899cc0SChe-liang Chiou static void tpm_write_byte(u8 value, u8 *ptr)
13590899cc0SChe-liang Chiou {
13690899cc0SChe-liang Chiou 	debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
13790899cc0SChe-liang Chiou 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value);
13890899cc0SChe-liang Chiou 	writeb(value, ptr);
13990899cc0SChe-liang Chiou }
14090899cc0SChe-liang Chiou 
14190899cc0SChe-liang Chiou static void tpm_write_word(u32 value, u32 *ptr)
14290899cc0SChe-liang Chiou {
14390899cc0SChe-liang Chiou 	debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
14490899cc0SChe-liang Chiou 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)lpc_tpm_dev, value);
14590899cc0SChe-liang Chiou 	writel(value, ptr);
14690899cc0SChe-liang Chiou }
14790899cc0SChe-liang Chiou 
14890899cc0SChe-liang Chiou /*
14990899cc0SChe-liang Chiou  * tis_wait_reg()
15090899cc0SChe-liang Chiou  *
15190899cc0SChe-liang Chiou  * Wait for at least a second for a register to change its state to match the
15290899cc0SChe-liang Chiou  * expected state. Normally the transition happens within microseconds.
15390899cc0SChe-liang Chiou  *
15490899cc0SChe-liang Chiou  * @reg - pointer to the TPM register
15590899cc0SChe-liang Chiou  * @mask - bitmask for the bitfield(s) to watch
15690899cc0SChe-liang Chiou  * @expected - value the field(s) are supposed to be set to
15790899cc0SChe-liang Chiou  *
15890899cc0SChe-liang Chiou  * Returns the register contents in case the expected value was found in the
15990899cc0SChe-liang Chiou  * appropriate register bits, or TPM_TIMEOUT_ERR on timeout.
16090899cc0SChe-liang Chiou  */
16190899cc0SChe-liang Chiou static u32 tis_wait_reg(u32 *reg, u8 mask, u8 expected)
16290899cc0SChe-liang Chiou {
16390899cc0SChe-liang Chiou 	u32 time_us = MAX_DELAY_US;
16490899cc0SChe-liang Chiou 
16590899cc0SChe-liang Chiou 	while (time_us > 0) {
16690899cc0SChe-liang Chiou 		u32 value = tpm_read_word(reg);
16790899cc0SChe-liang Chiou 		if ((value & mask) == expected)
16890899cc0SChe-liang Chiou 			return value;
16990899cc0SChe-liang Chiou 		udelay(1); /* 1 us */
17090899cc0SChe-liang Chiou 		time_us--;
17190899cc0SChe-liang Chiou 	}
17290899cc0SChe-liang Chiou 	return TPM_TIMEOUT_ERR;
17390899cc0SChe-liang Chiou }
17490899cc0SChe-liang Chiou 
17590899cc0SChe-liang Chiou /*
17690899cc0SChe-liang Chiou  * Probe the TPM device and try determining its manufacturer/device name.
17790899cc0SChe-liang Chiou  *
17890899cc0SChe-liang Chiou  * Returns 0 on success (the device is found or was found during an earlier
17990899cc0SChe-liang Chiou  * invocation) or TPM_DRIVER_ERR if the device is not found.
18090899cc0SChe-liang Chiou  */
18190899cc0SChe-liang Chiou int tis_init(void)
18290899cc0SChe-liang Chiou {
18390899cc0SChe-liang Chiou 	u32 didvid = tpm_read_word(&lpc_tpm_dev[0].did_vid);
18490899cc0SChe-liang Chiou 	int i;
18590899cc0SChe-liang Chiou 	const char *device_name = "unknown";
18690899cc0SChe-liang Chiou 	const char *vendor_name = device_name;
18790899cc0SChe-liang Chiou 	u16 vid, did;
18890899cc0SChe-liang Chiou 
18990899cc0SChe-liang Chiou 	if (vendor_dev_id)
19090899cc0SChe-liang Chiou 		return 0;  /* Already probed. */
19190899cc0SChe-liang Chiou 
19290899cc0SChe-liang Chiou 	if (!didvid || (didvid == 0xffffffff)) {
19390899cc0SChe-liang Chiou 		printf("%s: No TPM device found\n", __func__);
19490899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
19590899cc0SChe-liang Chiou 	}
19690899cc0SChe-liang Chiou 
19790899cc0SChe-liang Chiou 	vendor_dev_id = didvid;
19890899cc0SChe-liang Chiou 
19990899cc0SChe-liang Chiou 	vid = didvid & 0xffff;
20090899cc0SChe-liang Chiou 	did = (didvid >> 16) & 0xffff;
20190899cc0SChe-liang Chiou 	for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
20290899cc0SChe-liang Chiou 		int j = 0;
20390899cc0SChe-liang Chiou 		u16 known_did;
20490899cc0SChe-liang Chiou 
20590899cc0SChe-liang Chiou 		if (vid == vendor_names[i].vendor_id)
20690899cc0SChe-liang Chiou 			vendor_name = vendor_names[i].vendor_name;
20790899cc0SChe-liang Chiou 
20890899cc0SChe-liang Chiou 		while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) {
20990899cc0SChe-liang Chiou 			if (known_did == did) {
21090899cc0SChe-liang Chiou 				device_name =
21190899cc0SChe-liang Chiou 					vendor_names[i].dev_names[j].dev_name;
21290899cc0SChe-liang Chiou 				break;
21390899cc0SChe-liang Chiou 			}
21490899cc0SChe-liang Chiou 			j++;
21590899cc0SChe-liang Chiou 		}
21690899cc0SChe-liang Chiou 		break;
21790899cc0SChe-liang Chiou 	}
21890899cc0SChe-liang Chiou 
21990899cc0SChe-liang Chiou 	printf("Found TPM %s by %s\n", device_name, vendor_name);
22090899cc0SChe-liang Chiou 	return 0;
22190899cc0SChe-liang Chiou }
22290899cc0SChe-liang Chiou 
22390899cc0SChe-liang Chiou /*
22490899cc0SChe-liang Chiou  * tis_senddata()
22590899cc0SChe-liang Chiou  *
22690899cc0SChe-liang Chiou  * send the passed in data to the TPM device.
22790899cc0SChe-liang Chiou  *
22890899cc0SChe-liang Chiou  * @data - address of the data to send, byte by byte
22990899cc0SChe-liang Chiou  * @len - length of the data to send
23090899cc0SChe-liang Chiou  *
23190899cc0SChe-liang Chiou  * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
23290899cc0SChe-liang Chiou  * not accept the entire command).
23390899cc0SChe-liang Chiou  */
23490899cc0SChe-liang Chiou static u32 tis_senddata(const u8 * const data, u32 len)
23590899cc0SChe-liang Chiou {
23690899cc0SChe-liang Chiou 	u32 offset = 0;
23790899cc0SChe-liang Chiou 	u16 burst = 0;
23890899cc0SChe-liang Chiou 	u32 max_cycles = 0;
23990899cc0SChe-liang Chiou 	u8 locality = 0;
24090899cc0SChe-liang Chiou 	u32 value;
24190899cc0SChe-liang Chiou 
24290899cc0SChe-liang Chiou 	value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
24390899cc0SChe-liang Chiou 			     TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
24490899cc0SChe-liang Chiou 	if (value == TPM_TIMEOUT_ERR) {
24590899cc0SChe-liang Chiou 		printf("%s:%d - failed to get 'command_ready' status\n",
24690899cc0SChe-liang Chiou 		       __FILE__, __LINE__);
24790899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
24890899cc0SChe-liang Chiou 	}
24990899cc0SChe-liang Chiou 	burst = burst_count(value);
25090899cc0SChe-liang Chiou 
25190899cc0SChe-liang Chiou 	while (1) {
25290899cc0SChe-liang Chiou 		unsigned count;
25390899cc0SChe-liang Chiou 
25490899cc0SChe-liang Chiou 		/* Wait till the device is ready to accept more data. */
25590899cc0SChe-liang Chiou 		while (!burst) {
25690899cc0SChe-liang Chiou 			if (max_cycles++ == MAX_DELAY_US) {
25790899cc0SChe-liang Chiou 				printf("%s:%d failed to feed %d bytes of %d\n",
25890899cc0SChe-liang Chiou 				       __FILE__, __LINE__, len - offset, len);
25990899cc0SChe-liang Chiou 				return TPM_DRIVER_ERR;
26090899cc0SChe-liang Chiou 			}
26190899cc0SChe-liang Chiou 			udelay(1);
26290899cc0SChe-liang Chiou 			burst = burst_count(tpm_read_word(&lpc_tpm_dev
26390899cc0SChe-liang Chiou 						     [locality].tpm_status));
26490899cc0SChe-liang Chiou 		}
26590899cc0SChe-liang Chiou 
26690899cc0SChe-liang Chiou 		max_cycles = 0;
26790899cc0SChe-liang Chiou 
26890899cc0SChe-liang Chiou 		/*
26990899cc0SChe-liang Chiou 		 * Calculate number of bytes the TPM is ready to accept in one
27090899cc0SChe-liang Chiou 		 * shot.
27190899cc0SChe-liang Chiou 		 *
27290899cc0SChe-liang Chiou 		 * We want to send the last byte outside of the loop (hence
27390899cc0SChe-liang Chiou 		 * the -1 below) to make sure that the 'expected' status bit
27490899cc0SChe-liang Chiou 		 * changes to zero exactly after the last byte is fed into the
27590899cc0SChe-liang Chiou 		 * FIFO.
27690899cc0SChe-liang Chiou 		 */
27790899cc0SChe-liang Chiou 		count = min(burst, len - offset - 1);
27890899cc0SChe-liang Chiou 		while (count--)
27990899cc0SChe-liang Chiou 			tpm_write_byte(data[offset++],
28090899cc0SChe-liang Chiou 				  &lpc_tpm_dev[locality].data);
28190899cc0SChe-liang Chiou 
28290899cc0SChe-liang Chiou 		value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
28390899cc0SChe-liang Chiou 				     TIS_STS_VALID, TIS_STS_VALID);
28490899cc0SChe-liang Chiou 
28590899cc0SChe-liang Chiou 		if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) {
28690899cc0SChe-liang Chiou 			printf("%s:%d TPM command feed overflow\n",
28790899cc0SChe-liang Chiou 			       __FILE__, __LINE__);
28890899cc0SChe-liang Chiou 			return TPM_DRIVER_ERR;
28990899cc0SChe-liang Chiou 		}
29090899cc0SChe-liang Chiou 
29190899cc0SChe-liang Chiou 		burst = burst_count(value);
29290899cc0SChe-liang Chiou 		if ((offset == (len - 1)) && burst) {
29390899cc0SChe-liang Chiou 			/*
29490899cc0SChe-liang Chiou 			 * We need to be able to send the last byte to the
29590899cc0SChe-liang Chiou 			 * device, so burst size must be nonzero before we
29690899cc0SChe-liang Chiou 			 * break out.
29790899cc0SChe-liang Chiou 			 */
29890899cc0SChe-liang Chiou 			break;
29990899cc0SChe-liang Chiou 		}
30090899cc0SChe-liang Chiou 	}
30190899cc0SChe-liang Chiou 
30290899cc0SChe-liang Chiou 	/* Send the last byte. */
30390899cc0SChe-liang Chiou 	tpm_write_byte(data[offset++], &lpc_tpm_dev[locality].data);
30490899cc0SChe-liang Chiou 	/*
30590899cc0SChe-liang Chiou 	 * Verify that TPM does not expect any more data as part of this
30690899cc0SChe-liang Chiou 	 * command.
30790899cc0SChe-liang Chiou 	 */
30890899cc0SChe-liang Chiou 	value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
30990899cc0SChe-liang Chiou 			     TIS_STS_VALID, TIS_STS_VALID);
31090899cc0SChe-liang Chiou 	if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) {
31190899cc0SChe-liang Chiou 		printf("%s:%d unexpected TPM status 0x%x\n",
31290899cc0SChe-liang Chiou 		       __FILE__, __LINE__, value);
31390899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
31490899cc0SChe-liang Chiou 	}
31590899cc0SChe-liang Chiou 
31690899cc0SChe-liang Chiou 	/* OK, sitting pretty, let's start the command execution. */
31790899cc0SChe-liang Chiou 	tpm_write_word(TIS_STS_TPM_GO, &lpc_tpm_dev[locality].tpm_status);
31890899cc0SChe-liang Chiou 	return 0;
31990899cc0SChe-liang Chiou }
32090899cc0SChe-liang Chiou 
32190899cc0SChe-liang Chiou /*
32290899cc0SChe-liang Chiou  * tis_readresponse()
32390899cc0SChe-liang Chiou  *
32490899cc0SChe-liang Chiou  * read the TPM device response after a command was issued.
32590899cc0SChe-liang Chiou  *
32690899cc0SChe-liang Chiou  * @buffer - address where to read the response, byte by byte.
32790899cc0SChe-liang Chiou  * @len - pointer to the size of buffer
32890899cc0SChe-liang Chiou  *
32990899cc0SChe-liang Chiou  * On success stores the number of received bytes to len and returns 0. On
33090899cc0SChe-liang Chiou  * errors (misformatted TPM data or synchronization problems) returns
33190899cc0SChe-liang Chiou  * TPM_DRIVER_ERR.
33290899cc0SChe-liang Chiou  */
33390899cc0SChe-liang Chiou static u32 tis_readresponse(u8 *buffer, u32 *len)
33490899cc0SChe-liang Chiou {
33590899cc0SChe-liang Chiou 	u16 burst;
33690899cc0SChe-liang Chiou 	u32 value;
33790899cc0SChe-liang Chiou 	u32 offset = 0;
33890899cc0SChe-liang Chiou 	u8 locality = 0;
33990899cc0SChe-liang Chiou 	const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
34090899cc0SChe-liang Chiou 	u32 expected_count = *len;
34190899cc0SChe-liang Chiou 	int max_cycles = 0;
34290899cc0SChe-liang Chiou 
34390899cc0SChe-liang Chiou 	/* Wait for the TPM to process the command. */
34490899cc0SChe-liang Chiou 	value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
34590899cc0SChe-liang Chiou 			      has_data, has_data);
34690899cc0SChe-liang Chiou 	if (value == TPM_TIMEOUT_ERR) {
34790899cc0SChe-liang Chiou 		printf("%s:%d failed processing command\n",
34890899cc0SChe-liang Chiou 		       __FILE__, __LINE__);
34990899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
35090899cc0SChe-liang Chiou 	}
35190899cc0SChe-liang Chiou 
35290899cc0SChe-liang Chiou 	do {
35390899cc0SChe-liang Chiou 		while ((burst = burst_count(value)) == 0) {
35490899cc0SChe-liang Chiou 			if (max_cycles++ == MAX_DELAY_US) {
35590899cc0SChe-liang Chiou 				printf("%s:%d TPM stuck on read\n",
35690899cc0SChe-liang Chiou 				       __FILE__, __LINE__);
35790899cc0SChe-liang Chiou 				return TPM_DRIVER_ERR;
35890899cc0SChe-liang Chiou 			}
35990899cc0SChe-liang Chiou 			udelay(1);
36090899cc0SChe-liang Chiou 			value = tpm_read_word(&lpc_tpm_dev
36190899cc0SChe-liang Chiou 					      [locality].tpm_status);
36290899cc0SChe-liang Chiou 		}
36390899cc0SChe-liang Chiou 
36490899cc0SChe-liang Chiou 		max_cycles = 0;
36590899cc0SChe-liang Chiou 
36690899cc0SChe-liang Chiou 		while (burst-- && (offset < expected_count)) {
36790899cc0SChe-liang Chiou 			buffer[offset++] = tpm_read_byte(&lpc_tpm_dev
36890899cc0SChe-liang Chiou 							 [locality].data);
36990899cc0SChe-liang Chiou 
37090899cc0SChe-liang Chiou 			if (offset == 6) {
37190899cc0SChe-liang Chiou 				/*
37290899cc0SChe-liang Chiou 				 * We got the first six bytes of the reply,
37390899cc0SChe-liang Chiou 				 * let's figure out how many bytes to expect
37490899cc0SChe-liang Chiou 				 * total - it is stored as a 4 byte number in
37590899cc0SChe-liang Chiou 				 * network order, starting with offset 2 into
37690899cc0SChe-liang Chiou 				 * the body of the reply.
37790899cc0SChe-liang Chiou 				 */
37890899cc0SChe-liang Chiou 				u32 real_length;
37990899cc0SChe-liang Chiou 				memcpy(&real_length,
38090899cc0SChe-liang Chiou 				       buffer + 2,
38190899cc0SChe-liang Chiou 				       sizeof(real_length));
38290899cc0SChe-liang Chiou 				expected_count = be32_to_cpu(real_length);
38390899cc0SChe-liang Chiou 
38490899cc0SChe-liang Chiou 				if ((expected_count < offset) ||
38590899cc0SChe-liang Chiou 				    (expected_count > *len)) {
38690899cc0SChe-liang Chiou 					printf("%s:%d bad response size %d\n",
38790899cc0SChe-liang Chiou 					       __FILE__, __LINE__,
38890899cc0SChe-liang Chiou 					       expected_count);
38990899cc0SChe-liang Chiou 					return TPM_DRIVER_ERR;
39090899cc0SChe-liang Chiou 				}
39190899cc0SChe-liang Chiou 			}
39290899cc0SChe-liang Chiou 		}
39390899cc0SChe-liang Chiou 
39490899cc0SChe-liang Chiou 		/* Wait for the next portion. */
39590899cc0SChe-liang Chiou 		value = tis_wait_reg(&lpc_tpm_dev[locality].tpm_status,
39690899cc0SChe-liang Chiou 				     TIS_STS_VALID, TIS_STS_VALID);
39790899cc0SChe-liang Chiou 		if (value == TPM_TIMEOUT_ERR) {
39890899cc0SChe-liang Chiou 			printf("%s:%d failed to read response\n",
39990899cc0SChe-liang Chiou 			       __FILE__, __LINE__);
40090899cc0SChe-liang Chiou 			return TPM_DRIVER_ERR;
40190899cc0SChe-liang Chiou 		}
40290899cc0SChe-liang Chiou 
40390899cc0SChe-liang Chiou 		if (offset == expected_count)
40490899cc0SChe-liang Chiou 			break;	/* We got all we needed. */
40590899cc0SChe-liang Chiou 
40690899cc0SChe-liang Chiou 	} while ((value & has_data) == has_data);
40790899cc0SChe-liang Chiou 
40890899cc0SChe-liang Chiou 	/*
40990899cc0SChe-liang Chiou 	 * Make sure we indeed read all there was. The TIS_STS_VALID bit is
41090899cc0SChe-liang Chiou 	 * known to be set.
41190899cc0SChe-liang Chiou 	 */
41290899cc0SChe-liang Chiou 	if (value & TIS_STS_DATA_AVAILABLE) {
41390899cc0SChe-liang Chiou 		printf("%s:%d wrong receive status %x\n",
41490899cc0SChe-liang Chiou 		       __FILE__, __LINE__, value);
41590899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
41690899cc0SChe-liang Chiou 	}
41790899cc0SChe-liang Chiou 
41890899cc0SChe-liang Chiou 	/* Tell the TPM that we are done. */
41990899cc0SChe-liang Chiou 	tpm_write_word(TIS_STS_COMMAND_READY, &lpc_tpm_dev
42090899cc0SChe-liang Chiou 		  [locality].tpm_status);
42190899cc0SChe-liang Chiou 	*len = offset;
42290899cc0SChe-liang Chiou 	return 0;
42390899cc0SChe-liang Chiou }
42490899cc0SChe-liang Chiou 
42590899cc0SChe-liang Chiou int tis_open(void)
42690899cc0SChe-liang Chiou {
42790899cc0SChe-liang Chiou 	u8 locality = 0; /* we use locality zero for everything. */
42890899cc0SChe-liang Chiou 
42990899cc0SChe-liang Chiou 	if (tis_close())
43090899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
43190899cc0SChe-liang Chiou 
43290899cc0SChe-liang Chiou 	/* now request access to locality. */
43390899cc0SChe-liang Chiou 	tpm_write_word(TIS_ACCESS_REQUEST_USE, &lpc_tpm_dev[locality].access);
43490899cc0SChe-liang Chiou 
43590899cc0SChe-liang Chiou 	/* did we get a lock? */
43690899cc0SChe-liang Chiou 	if (tis_wait_reg(&lpc_tpm_dev[locality].access,
43790899cc0SChe-liang Chiou 			 TIS_ACCESS_ACTIVE_LOCALITY,
43890899cc0SChe-liang Chiou 			 TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) {
43990899cc0SChe-liang Chiou 		printf("%s:%d - failed to lock locality %d\n",
44090899cc0SChe-liang Chiou 		       __FILE__, __LINE__, locality);
44190899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
44290899cc0SChe-liang Chiou 	}
44390899cc0SChe-liang Chiou 
44490899cc0SChe-liang Chiou 	tpm_write_word(TIS_STS_COMMAND_READY,
44590899cc0SChe-liang Chiou 		       &lpc_tpm_dev[locality].tpm_status);
44690899cc0SChe-liang Chiou 	return 0;
44790899cc0SChe-liang Chiou }
44890899cc0SChe-liang Chiou 
44990899cc0SChe-liang Chiou int tis_close(void)
45090899cc0SChe-liang Chiou {
45190899cc0SChe-liang Chiou 	u8 locality = 0;
45290899cc0SChe-liang Chiou 
45390899cc0SChe-liang Chiou 	if (tpm_read_word(&lpc_tpm_dev[locality].access) &
45490899cc0SChe-liang Chiou 	    TIS_ACCESS_ACTIVE_LOCALITY) {
45590899cc0SChe-liang Chiou 		tpm_write_word(TIS_ACCESS_ACTIVE_LOCALITY,
45690899cc0SChe-liang Chiou 			       &lpc_tpm_dev[locality].access);
45790899cc0SChe-liang Chiou 
45890899cc0SChe-liang Chiou 		if (tis_wait_reg(&lpc_tpm_dev[locality].access,
45990899cc0SChe-liang Chiou 				 TIS_ACCESS_ACTIVE_LOCALITY, 0) ==
46090899cc0SChe-liang Chiou 		    TPM_TIMEOUT_ERR) {
46190899cc0SChe-liang Chiou 			printf("%s:%d - failed to release locality %d\n",
46290899cc0SChe-liang Chiou 			       __FILE__, __LINE__, locality);
46390899cc0SChe-liang Chiou 			return TPM_DRIVER_ERR;
46490899cc0SChe-liang Chiou 		}
46590899cc0SChe-liang Chiou 	}
46690899cc0SChe-liang Chiou 	return 0;
46790899cc0SChe-liang Chiou }
46890899cc0SChe-liang Chiou 
46990899cc0SChe-liang Chiou int tis_sendrecv(const u8 *sendbuf, size_t send_size,
47090899cc0SChe-liang Chiou 		 u8 *recvbuf, size_t *recv_len)
47190899cc0SChe-liang Chiou {
47290899cc0SChe-liang Chiou 	if (tis_senddata(sendbuf, send_size)) {
47390899cc0SChe-liang Chiou 		printf("%s:%d failed sending data to TPM\n",
47490899cc0SChe-liang Chiou 		       __FILE__, __LINE__);
47590899cc0SChe-liang Chiou 		return TPM_DRIVER_ERR;
47690899cc0SChe-liang Chiou 	}
47790899cc0SChe-liang Chiou 
47890899cc0SChe-liang Chiou 	return tis_readresponse(recvbuf, (u32 *)recv_len);
47990899cc0SChe-liang Chiou }
480