xref: /openbmc/u-boot/drivers/tpm/tpm_tis_lpc.c (revision 9450ab2b)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
290899cc0SChe-liang Chiou /*
390899cc0SChe-liang Chiou  * Copyright (c) 2011 The Chromium OS Authors.
490899cc0SChe-liang Chiou  */
590899cc0SChe-liang Chiou 
690899cc0SChe-liang Chiou /*
790899cc0SChe-liang Chiou  * The code in this file is based on the article "Writing a TPM Device Driver"
890899cc0SChe-liang Chiou  * published on http://ptgmedia.pearsoncmg.com.
990899cc0SChe-liang Chiou  *
1090899cc0SChe-liang Chiou  * One principal difference is that in the simplest config the other than 0
1190899cc0SChe-liang Chiou  * TPM localities do not get mapped by some devices (for instance, by Infineon
1290899cc0SChe-liang Chiou  * slb9635), so this driver provides access to locality 0 only.
1390899cc0SChe-liang Chiou  */
1490899cc0SChe-liang Chiou 
1590899cc0SChe-liang Chiou #include <common.h>
16d616ba5fSSimon Glass #include <dm.h>
17d616ba5fSSimon Glass #include <mapmem.h>
18d677bfe2SMiquel Raynal #include <tpm-v1.h>
19d616ba5fSSimon Glass #include <asm/io.h>
2090899cc0SChe-liang Chiou 
2190899cc0SChe-liang Chiou #define PREFIX "lpc_tpm: "
2290899cc0SChe-liang Chiou 
23a982b6f5SGeorge McCollister enum i2c_chip_type {
24a982b6f5SGeorge McCollister 	SLB9635,
25a982b6f5SGeorge McCollister 	AT97SC3204,
26a982b6f5SGeorge McCollister };
27a982b6f5SGeorge McCollister 
28a982b6f5SGeorge McCollister static const char * const chip_name[] = {
29a982b6f5SGeorge McCollister 	[SLB9635] = "Infineon SLB9635 TT 1.2",
30a982b6f5SGeorge McCollister 	[AT97SC3204] = "Atmel AT97SC3204",
31a982b6f5SGeorge McCollister };
32a982b6f5SGeorge McCollister 
33a982b6f5SGeorge McCollister static const u32 chip_didvid[] = {
34a982b6f5SGeorge McCollister 	[SLB9635] = 0xb15d1,
35a982b6f5SGeorge McCollister 	[AT97SC3204] = 0x32041114,
36a982b6f5SGeorge McCollister };
37a982b6f5SGeorge McCollister 
3890899cc0SChe-liang Chiou struct tpm_locality {
3990899cc0SChe-liang Chiou 	u32 access;
4090899cc0SChe-liang Chiou 	u8 padding0[4];
4190899cc0SChe-liang Chiou 	u32 int_enable;
4290899cc0SChe-liang Chiou 	u8 vector;
4390899cc0SChe-liang Chiou 	u8 padding1[3];
4490899cc0SChe-liang Chiou 	u32 int_status;
4590899cc0SChe-liang Chiou 	u32 int_capability;
4690899cc0SChe-liang Chiou 	u32 tpm_status;
4790899cc0SChe-liang Chiou 	u8 padding2[8];
4890899cc0SChe-liang Chiou 	u8 data;
4990899cc0SChe-liang Chiou 	u8 padding3[3803];
5090899cc0SChe-liang Chiou 	u32 did_vid;
5190899cc0SChe-liang Chiou 	u8 rid;
5290899cc0SChe-liang Chiou 	u8 padding4[251];
5390899cc0SChe-liang Chiou };
5490899cc0SChe-liang Chiou 
55d616ba5fSSimon Glass struct tpm_tis_lpc_priv {
56d616ba5fSSimon Glass 	struct tpm_locality *regs;
57d616ba5fSSimon Glass };
58d616ba5fSSimon Glass 
5990899cc0SChe-liang Chiou /*
6090899cc0SChe-liang Chiou  * This pointer refers to the TPM chip, 5 of its localities are mapped as an
6190899cc0SChe-liang Chiou  * array.
6290899cc0SChe-liang Chiou  */
6390899cc0SChe-liang Chiou #define TPM_TOTAL_LOCALITIES	5
6490899cc0SChe-liang Chiou 
6590899cc0SChe-liang Chiou /* Some registers' bit field definitions */
6690899cc0SChe-liang Chiou #define TIS_STS_VALID                  (1 << 7) /* 0x80 */
6790899cc0SChe-liang Chiou #define TIS_STS_COMMAND_READY          (1 << 6) /* 0x40 */
6890899cc0SChe-liang Chiou #define TIS_STS_TPM_GO                 (1 << 5) /* 0x20 */
6990899cc0SChe-liang Chiou #define TIS_STS_DATA_AVAILABLE         (1 << 4) /* 0x10 */
7090899cc0SChe-liang Chiou #define TIS_STS_EXPECT                 (1 << 3) /* 0x08 */
7190899cc0SChe-liang Chiou #define TIS_STS_RESPONSE_RETRY         (1 << 1) /* 0x02 */
7290899cc0SChe-liang Chiou 
7390899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_REG_VALID_STS   (1 << 7) /* 0x80 */
7490899cc0SChe-liang Chiou #define TIS_ACCESS_ACTIVE_LOCALITY     (1 << 5) /* 0x20 */
7590899cc0SChe-liang Chiou #define TIS_ACCESS_BEEN_SEIZED         (1 << 4) /* 0x10 */
7690899cc0SChe-liang Chiou #define TIS_ACCESS_SEIZE               (1 << 3) /* 0x08 */
7790899cc0SChe-liang Chiou #define TIS_ACCESS_PENDING_REQUEST     (1 << 2) /* 0x04 */
7890899cc0SChe-liang Chiou #define TIS_ACCESS_REQUEST_USE         (1 << 1) /* 0x02 */
7990899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_ESTABLISHMENT   (1 << 0) /* 0x01 */
8090899cc0SChe-liang Chiou 
8190899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_MASK       (0xffff)
8290899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_SHIFT      (8)
8390899cc0SChe-liang Chiou 
8490899cc0SChe-liang Chiou  /* 1 second is plenty for anything TPM does. */
8590899cc0SChe-liang Chiou #define MAX_DELAY_US	(1000 * 1000)
8690899cc0SChe-liang Chiou 
8790899cc0SChe-liang Chiou /* Retrieve burst count value out of the status register contents. */
burst_count(u32 status)8890899cc0SChe-liang Chiou static u16 burst_count(u32 status)
8990899cc0SChe-liang Chiou {
90d616ba5fSSimon Glass 	return (status >> TIS_STS_BURST_COUNT_SHIFT) &
91d616ba5fSSimon Glass 			TIS_STS_BURST_COUNT_MASK;
9290899cc0SChe-liang Chiou }
9390899cc0SChe-liang Chiou 
9490899cc0SChe-liang Chiou /* TPM access wrappers to support tracing */
tpm_read_byte(struct tpm_tis_lpc_priv * priv,const u8 * ptr)95d616ba5fSSimon Glass static u8 tpm_read_byte(struct tpm_tis_lpc_priv *priv, const u8 *ptr)
9690899cc0SChe-liang Chiou {
9790899cc0SChe-liang Chiou 	u8  ret = readb(ptr);
9890899cc0SChe-liang Chiou 	debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
99d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
10090899cc0SChe-liang Chiou 	return ret;
10190899cc0SChe-liang Chiou }
10290899cc0SChe-liang Chiou 
tpm_read_word(struct tpm_tis_lpc_priv * priv,const u32 * ptr)103d616ba5fSSimon Glass static u32 tpm_read_word(struct tpm_tis_lpc_priv *priv, const u32 *ptr)
10490899cc0SChe-liang Chiou {
10590899cc0SChe-liang Chiou 	u32  ret = readl(ptr);
10690899cc0SChe-liang Chiou 	debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
107d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
10890899cc0SChe-liang Chiou 	return ret;
10990899cc0SChe-liang Chiou }
11090899cc0SChe-liang Chiou 
tpm_write_byte(struct tpm_tis_lpc_priv * priv,u8 value,u8 * ptr)111d616ba5fSSimon Glass static void tpm_write_byte(struct tpm_tis_lpc_priv *priv, u8 value, u8 *ptr)
11290899cc0SChe-liang Chiou {
11390899cc0SChe-liang Chiou 	debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
114d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
11590899cc0SChe-liang Chiou 	writeb(value, ptr);
11690899cc0SChe-liang Chiou }
11790899cc0SChe-liang Chiou 
tpm_write_word(struct tpm_tis_lpc_priv * priv,u32 value,u32 * ptr)118d616ba5fSSimon Glass static void tpm_write_word(struct tpm_tis_lpc_priv *priv, u32 value,
119d616ba5fSSimon Glass 			   u32 *ptr)
12090899cc0SChe-liang Chiou {
12190899cc0SChe-liang Chiou 	debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
122d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
12390899cc0SChe-liang Chiou 	writel(value, ptr);
12490899cc0SChe-liang Chiou }
12590899cc0SChe-liang Chiou 
12690899cc0SChe-liang Chiou /*
12790899cc0SChe-liang Chiou  * tis_wait_reg()
12890899cc0SChe-liang Chiou  *
12990899cc0SChe-liang Chiou  * Wait for at least a second for a register to change its state to match the
13090899cc0SChe-liang Chiou  * expected state. Normally the transition happens within microseconds.
13190899cc0SChe-liang Chiou  *
13290899cc0SChe-liang Chiou  * @reg - pointer to the TPM register
13390899cc0SChe-liang Chiou  * @mask - bitmask for the bitfield(s) to watch
13490899cc0SChe-liang Chiou  * @expected - value the field(s) are supposed to be set to
13590899cc0SChe-liang Chiou  *
13690899cc0SChe-liang Chiou  * Returns the register contents in case the expected value was found in the
137d616ba5fSSimon Glass  * appropriate register bits, or -ETIMEDOUT on timeout.
13890899cc0SChe-liang Chiou  */
tis_wait_reg(struct tpm_tis_lpc_priv * priv,u32 * reg,u8 mask,u8 expected)139d616ba5fSSimon Glass static int tis_wait_reg(struct tpm_tis_lpc_priv *priv, u32 *reg, u8 mask,
140d616ba5fSSimon Glass 			u8 expected)
14190899cc0SChe-liang Chiou {
14290899cc0SChe-liang Chiou 	u32 time_us = MAX_DELAY_US;
14390899cc0SChe-liang Chiou 
14490899cc0SChe-liang Chiou 	while (time_us > 0) {
145d616ba5fSSimon Glass 		u32 value = tpm_read_word(priv, reg);
14690899cc0SChe-liang Chiou 		if ((value & mask) == expected)
14790899cc0SChe-liang Chiou 			return value;
14890899cc0SChe-liang Chiou 		udelay(1); /* 1 us */
14990899cc0SChe-liang Chiou 		time_us--;
15090899cc0SChe-liang Chiou 	}
151d616ba5fSSimon Glass 
152d616ba5fSSimon Glass 	return -ETIMEDOUT;
15390899cc0SChe-liang Chiou }
15490899cc0SChe-liang Chiou 
15590899cc0SChe-liang Chiou /*
15690899cc0SChe-liang Chiou  * Probe the TPM device and try determining its manufacturer/device name.
15790899cc0SChe-liang Chiou  *
158d616ba5fSSimon Glass  * Returns 0 on success, -ve on error
15990899cc0SChe-liang Chiou  */
tpm_tis_lpc_probe(struct udevice * dev)160d616ba5fSSimon Glass static int tpm_tis_lpc_probe(struct udevice *dev)
16190899cc0SChe-liang Chiou {
162d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
163d616ba5fSSimon Glass 	fdt_addr_t addr;
164d616ba5fSSimon Glass 	u32 didvid;
165a982b6f5SGeorge McCollister 	ulong chip_type = dev_get_driver_data(dev);
16690899cc0SChe-liang Chiou 
167c89d32a7SSimon Glass 	addr = dev_read_addr(dev);
168d616ba5fSSimon Glass 	if (addr == FDT_ADDR_T_NONE)
169d616ba5fSSimon Glass 		return -EINVAL;
170d616ba5fSSimon Glass 	priv->regs = map_sysmem(addr, 0);
171d616ba5fSSimon Glass 	didvid = tpm_read_word(priv, &priv->regs[0].did_vid);
17290899cc0SChe-liang Chiou 
173a982b6f5SGeorge McCollister 	if (didvid != chip_didvid[chip_type]) {
174a982b6f5SGeorge McCollister 		u32 vid, did;
17590899cc0SChe-liang Chiou 		vid = didvid & 0xffff;
17690899cc0SChe-liang Chiou 		did = (didvid >> 16) & 0xffff;
177d616ba5fSSimon Glass 		debug("Invalid vendor/device ID %04x/%04x\n", vid, did);
178a982b6f5SGeorge McCollister 		return -ENODEV;
17990899cc0SChe-liang Chiou 	}
18090899cc0SChe-liang Chiou 
181a982b6f5SGeorge McCollister 	debug("Found TPM: %s\n", chip_name[chip_type]);
182d616ba5fSSimon Glass 
18390899cc0SChe-liang Chiou 	return 0;
18490899cc0SChe-liang Chiou }
18590899cc0SChe-liang Chiou 
18690899cc0SChe-liang Chiou /*
18790899cc0SChe-liang Chiou  * tis_senddata()
18890899cc0SChe-liang Chiou  *
18990899cc0SChe-liang Chiou  * send the passed in data to the TPM device.
19090899cc0SChe-liang Chiou  *
19190899cc0SChe-liang Chiou  * @data - address of the data to send, byte by byte
19290899cc0SChe-liang Chiou  * @len - length of the data to send
19390899cc0SChe-liang Chiou  *
194d616ba5fSSimon Glass  * Returns 0 on success, -ve on error (in case the device does not accept
195d616ba5fSSimon Glass  * the entire command).
19690899cc0SChe-liang Chiou  */
tis_senddata(struct udevice * dev,const u8 * data,size_t len)197d616ba5fSSimon Glass static int tis_senddata(struct udevice *dev, const u8 *data, size_t len)
19890899cc0SChe-liang Chiou {
199d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
200d616ba5fSSimon Glass 	struct tpm_locality *regs = priv->regs;
20190899cc0SChe-liang Chiou 	u32 offset = 0;
20290899cc0SChe-liang Chiou 	u16 burst = 0;
20390899cc0SChe-liang Chiou 	u32 max_cycles = 0;
20490899cc0SChe-liang Chiou 	u8 locality = 0;
20590899cc0SChe-liang Chiou 	u32 value;
20690899cc0SChe-liang Chiou 
207d616ba5fSSimon Glass 	value = tis_wait_reg(priv, &regs[locality].tpm_status,
20890899cc0SChe-liang Chiou 			     TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
209d616ba5fSSimon Glass 	if (value == -ETIMEDOUT) {
21090899cc0SChe-liang Chiou 		printf("%s:%d - failed to get 'command_ready' status\n",
21190899cc0SChe-liang Chiou 		       __FILE__, __LINE__);
212d616ba5fSSimon Glass 		return value;
21390899cc0SChe-liang Chiou 	}
21490899cc0SChe-liang Chiou 	burst = burst_count(value);
21590899cc0SChe-liang Chiou 
21690899cc0SChe-liang Chiou 	while (1) {
21790899cc0SChe-liang Chiou 		unsigned count;
21890899cc0SChe-liang Chiou 
21990899cc0SChe-liang Chiou 		/* Wait till the device is ready to accept more data. */
22090899cc0SChe-liang Chiou 		while (!burst) {
22190899cc0SChe-liang Chiou 			if (max_cycles++ == MAX_DELAY_US) {
22222230e91SSimon Glass 				printf("%s:%d failed to feed %zd bytes of %zd\n",
22390899cc0SChe-liang Chiou 				       __FILE__, __LINE__, len - offset, len);
224d616ba5fSSimon Glass 				return -ETIMEDOUT;
22590899cc0SChe-liang Chiou 			}
22690899cc0SChe-liang Chiou 			udelay(1);
227d616ba5fSSimon Glass 			burst = burst_count(tpm_read_word(priv,
228d616ba5fSSimon Glass 					&regs[locality].tpm_status));
22990899cc0SChe-liang Chiou 		}
23090899cc0SChe-liang Chiou 
23190899cc0SChe-liang Chiou 		max_cycles = 0;
23290899cc0SChe-liang Chiou 
23390899cc0SChe-liang Chiou 		/*
23490899cc0SChe-liang Chiou 		 * Calculate number of bytes the TPM is ready to accept in one
23590899cc0SChe-liang Chiou 		 * shot.
23690899cc0SChe-liang Chiou 		 *
23790899cc0SChe-liang Chiou 		 * We want to send the last byte outside of the loop (hence
23890899cc0SChe-liang Chiou 		 * the -1 below) to make sure that the 'expected' status bit
23990899cc0SChe-liang Chiou 		 * changes to zero exactly after the last byte is fed into the
24090899cc0SChe-liang Chiou 		 * FIFO.
24190899cc0SChe-liang Chiou 		 */
24222230e91SSimon Glass 		count = min((size_t)burst, len - offset - 1);
24390899cc0SChe-liang Chiou 		while (count--)
244d616ba5fSSimon Glass 			tpm_write_byte(priv, data[offset++],
245d616ba5fSSimon Glass 				       &regs[locality].data);
24690899cc0SChe-liang Chiou 
247d616ba5fSSimon Glass 		value = tis_wait_reg(priv, &regs[locality].tpm_status,
24890899cc0SChe-liang Chiou 				     TIS_STS_VALID, TIS_STS_VALID);
24990899cc0SChe-liang Chiou 
250d616ba5fSSimon Glass 		if ((value == -ETIMEDOUT) || !(value & TIS_STS_EXPECT)) {
25190899cc0SChe-liang Chiou 			printf("%s:%d TPM command feed overflow\n",
25290899cc0SChe-liang Chiou 			       __FILE__, __LINE__);
253d616ba5fSSimon Glass 			return value == -ETIMEDOUT ? value : -EIO;
25490899cc0SChe-liang Chiou 		}
25590899cc0SChe-liang Chiou 
25690899cc0SChe-liang Chiou 		burst = burst_count(value);
25790899cc0SChe-liang Chiou 		if ((offset == (len - 1)) && burst) {
25890899cc0SChe-liang Chiou 			/*
25990899cc0SChe-liang Chiou 			 * We need to be able to send the last byte to the
26090899cc0SChe-liang Chiou 			 * device, so burst size must be nonzero before we
26190899cc0SChe-liang Chiou 			 * break out.
26290899cc0SChe-liang Chiou 			 */
26390899cc0SChe-liang Chiou 			break;
26490899cc0SChe-liang Chiou 		}
26590899cc0SChe-liang Chiou 	}
26690899cc0SChe-liang Chiou 
26790899cc0SChe-liang Chiou 	/* Send the last byte. */
268d616ba5fSSimon Glass 	tpm_write_byte(priv, data[offset++], &regs[locality].data);
26990899cc0SChe-liang Chiou 	/*
27090899cc0SChe-liang Chiou 	 * Verify that TPM does not expect any more data as part of this
27190899cc0SChe-liang Chiou 	 * command.
27290899cc0SChe-liang Chiou 	 */
273d616ba5fSSimon Glass 	value = tis_wait_reg(priv, &regs[locality].tpm_status,
27490899cc0SChe-liang Chiou 			     TIS_STS_VALID, TIS_STS_VALID);
275d616ba5fSSimon Glass 	if ((value == -ETIMEDOUT) || (value & TIS_STS_EXPECT)) {
27690899cc0SChe-liang Chiou 		printf("%s:%d unexpected TPM status 0x%x\n",
27790899cc0SChe-liang Chiou 		       __FILE__, __LINE__, value);
278d616ba5fSSimon Glass 		return value == -ETIMEDOUT ? value : -EIO;
27990899cc0SChe-liang Chiou 	}
28090899cc0SChe-liang Chiou 
28190899cc0SChe-liang Chiou 	/* OK, sitting pretty, let's start the command execution. */
282d616ba5fSSimon Glass 	tpm_write_word(priv, TIS_STS_TPM_GO, &regs[locality].tpm_status);
28390899cc0SChe-liang Chiou 	return 0;
28490899cc0SChe-liang Chiou }
28590899cc0SChe-liang Chiou 
28690899cc0SChe-liang Chiou /*
28790899cc0SChe-liang Chiou  * tis_readresponse()
28890899cc0SChe-liang Chiou  *
28990899cc0SChe-liang Chiou  * read the TPM device response after a command was issued.
29090899cc0SChe-liang Chiou  *
29190899cc0SChe-liang Chiou  * @buffer - address where to read the response, byte by byte.
29290899cc0SChe-liang Chiou  * @len - pointer to the size of buffer
29390899cc0SChe-liang Chiou  *
29490899cc0SChe-liang Chiou  * On success stores the number of received bytes to len and returns 0. On
29590899cc0SChe-liang Chiou  * errors (misformatted TPM data or synchronization problems) returns
296d616ba5fSSimon Glass  * -ve value.
29790899cc0SChe-liang Chiou  */
tis_readresponse(struct udevice * dev,u8 * buffer,size_t len)298d616ba5fSSimon Glass static int tis_readresponse(struct udevice *dev, u8 *buffer, size_t len)
29990899cc0SChe-liang Chiou {
300d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
301d616ba5fSSimon Glass 	struct tpm_locality *regs = priv->regs;
30290899cc0SChe-liang Chiou 	u16 burst;
30390899cc0SChe-liang Chiou 	u32 value;
30490899cc0SChe-liang Chiou 	u32 offset = 0;
30590899cc0SChe-liang Chiou 	u8 locality = 0;
30690899cc0SChe-liang Chiou 	const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
307d616ba5fSSimon Glass 	u32 expected_count = len;
30890899cc0SChe-liang Chiou 	int max_cycles = 0;
30990899cc0SChe-liang Chiou 
31090899cc0SChe-liang Chiou 	/* Wait for the TPM to process the command. */
311d616ba5fSSimon Glass 	value = tis_wait_reg(priv, &regs[locality].tpm_status,
31290899cc0SChe-liang Chiou 			      has_data, has_data);
313d616ba5fSSimon Glass 	if (value == -ETIMEDOUT) {
31490899cc0SChe-liang Chiou 		printf("%s:%d failed processing command\n",
31590899cc0SChe-liang Chiou 		       __FILE__, __LINE__);
316d616ba5fSSimon Glass 		return value;
31790899cc0SChe-liang Chiou 	}
31890899cc0SChe-liang Chiou 
31990899cc0SChe-liang Chiou 	do {
32090899cc0SChe-liang Chiou 		while ((burst = burst_count(value)) == 0) {
32190899cc0SChe-liang Chiou 			if (max_cycles++ == MAX_DELAY_US) {
32290899cc0SChe-liang Chiou 				printf("%s:%d TPM stuck on read\n",
32390899cc0SChe-liang Chiou 				       __FILE__, __LINE__);
324d616ba5fSSimon Glass 				return -EIO;
32590899cc0SChe-liang Chiou 			}
32690899cc0SChe-liang Chiou 			udelay(1);
327d616ba5fSSimon Glass 			value = tpm_read_word(priv, &regs[locality].tpm_status);
32890899cc0SChe-liang Chiou 		}
32990899cc0SChe-liang Chiou 
33090899cc0SChe-liang Chiou 		max_cycles = 0;
33190899cc0SChe-liang Chiou 
33290899cc0SChe-liang Chiou 		while (burst-- && (offset < expected_count)) {
333d616ba5fSSimon Glass 			buffer[offset++] = tpm_read_byte(priv,
334d616ba5fSSimon Glass 						&regs[locality].data);
33590899cc0SChe-liang Chiou 
33690899cc0SChe-liang Chiou 			if (offset == 6) {
33790899cc0SChe-liang Chiou 				/*
33890899cc0SChe-liang Chiou 				 * We got the first six bytes of the reply,
33990899cc0SChe-liang Chiou 				 * let's figure out how many bytes to expect
34090899cc0SChe-liang Chiou 				 * total - it is stored as a 4 byte number in
34190899cc0SChe-liang Chiou 				 * network order, starting with offset 2 into
34290899cc0SChe-liang Chiou 				 * the body of the reply.
34390899cc0SChe-liang Chiou 				 */
34490899cc0SChe-liang Chiou 				u32 real_length;
34590899cc0SChe-liang Chiou 				memcpy(&real_length,
34690899cc0SChe-liang Chiou 				       buffer + 2,
34790899cc0SChe-liang Chiou 				       sizeof(real_length));
34890899cc0SChe-liang Chiou 				expected_count = be32_to_cpu(real_length);
34990899cc0SChe-liang Chiou 
35090899cc0SChe-liang Chiou 				if ((expected_count < offset) ||
351d616ba5fSSimon Glass 				    (expected_count > len)) {
35290899cc0SChe-liang Chiou 					printf("%s:%d bad response size %d\n",
35390899cc0SChe-liang Chiou 					       __FILE__, __LINE__,
35490899cc0SChe-liang Chiou 					       expected_count);
355d616ba5fSSimon Glass 					return -ENOSPC;
35690899cc0SChe-liang Chiou 				}
35790899cc0SChe-liang Chiou 			}
35890899cc0SChe-liang Chiou 		}
35990899cc0SChe-liang Chiou 
36090899cc0SChe-liang Chiou 		/* Wait for the next portion. */
361d616ba5fSSimon Glass 		value = tis_wait_reg(priv, &regs[locality].tpm_status,
36290899cc0SChe-liang Chiou 				     TIS_STS_VALID, TIS_STS_VALID);
363d616ba5fSSimon Glass 		if (value == -ETIMEDOUT) {
36490899cc0SChe-liang Chiou 			printf("%s:%d failed to read response\n",
36590899cc0SChe-liang Chiou 			       __FILE__, __LINE__);
366d616ba5fSSimon Glass 			return value;
36790899cc0SChe-liang Chiou 		}
36890899cc0SChe-liang Chiou 
36990899cc0SChe-liang Chiou 		if (offset == expected_count)
37090899cc0SChe-liang Chiou 			break;	/* We got all we needed. */
37190899cc0SChe-liang Chiou 
37290899cc0SChe-liang Chiou 	} while ((value & has_data) == has_data);
37390899cc0SChe-liang Chiou 
37490899cc0SChe-liang Chiou 	/*
37590899cc0SChe-liang Chiou 	 * Make sure we indeed read all there was. The TIS_STS_VALID bit is
37690899cc0SChe-liang Chiou 	 * known to be set.
37790899cc0SChe-liang Chiou 	 */
37890899cc0SChe-liang Chiou 	if (value & TIS_STS_DATA_AVAILABLE) {
37990899cc0SChe-liang Chiou 		printf("%s:%d wrong receive status %x\n",
38090899cc0SChe-liang Chiou 		       __FILE__, __LINE__, value);
381d616ba5fSSimon Glass 		return -EBADMSG;
38290899cc0SChe-liang Chiou 	}
38390899cc0SChe-liang Chiou 
38490899cc0SChe-liang Chiou 	/* Tell the TPM that we are done. */
385d616ba5fSSimon Glass 	tpm_write_word(priv, TIS_STS_COMMAND_READY,
386d616ba5fSSimon Glass 		       &regs[locality].tpm_status);
387d616ba5fSSimon Glass 
388d616ba5fSSimon Glass 	return offset;
38990899cc0SChe-liang Chiou }
39090899cc0SChe-liang Chiou 
tpm_tis_lpc_close(struct udevice * dev)391d616ba5fSSimon Glass static int tpm_tis_lpc_close(struct udevice *dev)
39290899cc0SChe-liang Chiou {
393d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
394d616ba5fSSimon Glass 	struct tpm_locality *regs = priv->regs;
39590899cc0SChe-liang Chiou 	u8 locality = 0;
39690899cc0SChe-liang Chiou 
397d616ba5fSSimon Glass 	if (tpm_read_word(priv, &regs[locality].access) &
39890899cc0SChe-liang Chiou 	    TIS_ACCESS_ACTIVE_LOCALITY) {
399d616ba5fSSimon Glass 		tpm_write_word(priv, TIS_ACCESS_ACTIVE_LOCALITY,
400d616ba5fSSimon Glass 			       &regs[locality].access);
40190899cc0SChe-liang Chiou 
402d616ba5fSSimon Glass 		if (tis_wait_reg(priv, &regs[locality].access,
403d616ba5fSSimon Glass 				 TIS_ACCESS_ACTIVE_LOCALITY, 0) == -ETIMEDOUT) {
40490899cc0SChe-liang Chiou 			printf("%s:%d - failed to release locality %d\n",
40590899cc0SChe-liang Chiou 			       __FILE__, __LINE__, locality);
406d616ba5fSSimon Glass 			return -ETIMEDOUT;
40790899cc0SChe-liang Chiou 		}
40890899cc0SChe-liang Chiou 	}
40990899cc0SChe-liang Chiou 	return 0;
41090899cc0SChe-liang Chiou }
41190899cc0SChe-liang Chiou 
tpm_tis_lpc_open(struct udevice * dev)412*51f00c17SSimon Glass static int tpm_tis_lpc_open(struct udevice *dev)
413*51f00c17SSimon Glass {
414*51f00c17SSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
415*51f00c17SSimon Glass 	struct tpm_locality *regs = priv->regs;
416*51f00c17SSimon Glass 	u8 locality = 0; /* we use locality zero for everything. */
417*51f00c17SSimon Glass 	int ret;
418*51f00c17SSimon Glass 
419*51f00c17SSimon Glass 	ret = tpm_tis_lpc_close(dev);
420*51f00c17SSimon Glass 	if (ret) {
421*51f00c17SSimon Glass 		printf("%s: Failed to close TPM\n", __func__);
422*51f00c17SSimon Glass 		return ret;
423*51f00c17SSimon Glass 	}
424*51f00c17SSimon Glass 
425*51f00c17SSimon Glass 	/* now request access to locality. */
426*51f00c17SSimon Glass 	tpm_write_word(priv, TIS_ACCESS_REQUEST_USE, &regs[locality].access);
427*51f00c17SSimon Glass 
428*51f00c17SSimon Glass 	/* did we get a lock? */
429*51f00c17SSimon Glass 	ret = tis_wait_reg(priv, &regs[locality].access,
430*51f00c17SSimon Glass 			 TIS_ACCESS_ACTIVE_LOCALITY,
431*51f00c17SSimon Glass 			 TIS_ACCESS_ACTIVE_LOCALITY);
432*51f00c17SSimon Glass 	if (ret == -ETIMEDOUT) {
433*51f00c17SSimon Glass 		printf("%s:%d - failed to lock locality %d\n",
434*51f00c17SSimon Glass 		       __FILE__, __LINE__, locality);
435*51f00c17SSimon Glass 		return ret;
436*51f00c17SSimon Glass 	}
437*51f00c17SSimon Glass 
438*51f00c17SSimon Glass 	tpm_write_word(priv, TIS_STS_COMMAND_READY,
439*51f00c17SSimon Glass 		       &regs[locality].tpm_status);
440*51f00c17SSimon Glass 
441*51f00c17SSimon Glass 	return 0;
442*51f00c17SSimon Glass }
443*51f00c17SSimon Glass 
tpm_tis_get_desc(struct udevice * dev,char * buf,int size)444d616ba5fSSimon Glass static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
44590899cc0SChe-liang Chiou {
446a982b6f5SGeorge McCollister 	ulong chip_type = dev_get_driver_data(dev);
447a982b6f5SGeorge McCollister 
448d616ba5fSSimon Glass 	if (size < 50)
449d616ba5fSSimon Glass 		return -ENOSPC;
450d616ba5fSSimon Glass 
451a982b6f5SGeorge McCollister 	return snprintf(buf, size, "1.2 TPM (%s)",
452a982b6f5SGeorge McCollister 			chip_name[chip_type]);
45390899cc0SChe-liang Chiou }
45490899cc0SChe-liang Chiou 
455d616ba5fSSimon Glass 
456d616ba5fSSimon Glass static const struct tpm_ops tpm_tis_lpc_ops = {
457d616ba5fSSimon Glass 	.open		= tpm_tis_lpc_open,
458d616ba5fSSimon Glass 	.close		= tpm_tis_lpc_close,
459d616ba5fSSimon Glass 	.get_desc	= tpm_tis_get_desc,
460d616ba5fSSimon Glass 	.send		= tis_senddata,
461d616ba5fSSimon Glass 	.recv		= tis_readresponse,
462d616ba5fSSimon Glass };
463d616ba5fSSimon Glass 
464d616ba5fSSimon Glass static const struct udevice_id tpm_tis_lpc_ids[] = {
465a982b6f5SGeorge McCollister 	{ .compatible = "infineon,slb9635lpc", .data = SLB9635 },
466a982b6f5SGeorge McCollister 	{ .compatible = "atmel,at97sc3204", .data = AT97SC3204 },
467d616ba5fSSimon Glass 	{ }
468d616ba5fSSimon Glass };
469d616ba5fSSimon Glass 
470d616ba5fSSimon Glass U_BOOT_DRIVER(tpm_tis_lpc) = {
471d616ba5fSSimon Glass 	.name   = "tpm_tis_lpc",
472d616ba5fSSimon Glass 	.id     = UCLASS_TPM,
473d616ba5fSSimon Glass 	.of_match = tpm_tis_lpc_ids,
474d616ba5fSSimon Glass 	.ops    = &tpm_tis_lpc_ops,
475d616ba5fSSimon Glass 	.probe	= tpm_tis_lpc_probe,
476d616ba5fSSimon Glass 	.priv_auto_alloc_size = sizeof(struct tpm_tis_lpc_priv),
477d616ba5fSSimon Glass };
478