xref: /openbmc/u-boot/drivers/tpm/tpm_tis_lpc.c (revision a982b6f5)
190899cc0SChe-liang Chiou /*
290899cc0SChe-liang Chiou  * Copyright (c) 2011 The Chromium OS Authors.
390899cc0SChe-liang Chiou  *
41a459660SWolfgang 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>
17d616ba5fSSimon Glass #include <dm.h>
18d616ba5fSSimon Glass #include <mapmem.h>
1990899cc0SChe-liang Chiou #include <tpm.h>
20d616ba5fSSimon Glass #include <asm/io.h>
2190899cc0SChe-liang Chiou 
2290899cc0SChe-liang Chiou #define PREFIX "lpc_tpm: "
2390899cc0SChe-liang Chiou 
24*a982b6f5SGeorge McCollister enum i2c_chip_type {
25*a982b6f5SGeorge McCollister 	SLB9635,
26*a982b6f5SGeorge McCollister 	AT97SC3204,
27*a982b6f5SGeorge McCollister };
28*a982b6f5SGeorge McCollister 
29*a982b6f5SGeorge McCollister static const char * const chip_name[] = {
30*a982b6f5SGeorge McCollister 	[SLB9635] = "Infineon SLB9635 TT 1.2",
31*a982b6f5SGeorge McCollister 	[AT97SC3204] = "Atmel AT97SC3204",
32*a982b6f5SGeorge McCollister };
33*a982b6f5SGeorge McCollister 
34*a982b6f5SGeorge McCollister static const u32 chip_didvid[] = {
35*a982b6f5SGeorge McCollister 	[SLB9635] = 0xb15d1,
36*a982b6f5SGeorge McCollister 	[AT97SC3204] = 0x32041114,
37*a982b6f5SGeorge McCollister };
38*a982b6f5SGeorge McCollister 
3990899cc0SChe-liang Chiou struct tpm_locality {
4090899cc0SChe-liang Chiou 	u32 access;
4190899cc0SChe-liang Chiou 	u8 padding0[4];
4290899cc0SChe-liang Chiou 	u32 int_enable;
4390899cc0SChe-liang Chiou 	u8 vector;
4490899cc0SChe-liang Chiou 	u8 padding1[3];
4590899cc0SChe-liang Chiou 	u32 int_status;
4690899cc0SChe-liang Chiou 	u32 int_capability;
4790899cc0SChe-liang Chiou 	u32 tpm_status;
4890899cc0SChe-liang Chiou 	u8 padding2[8];
4990899cc0SChe-liang Chiou 	u8 data;
5090899cc0SChe-liang Chiou 	u8 padding3[3803];
5190899cc0SChe-liang Chiou 	u32 did_vid;
5290899cc0SChe-liang Chiou 	u8 rid;
5390899cc0SChe-liang Chiou 	u8 padding4[251];
5490899cc0SChe-liang Chiou };
5590899cc0SChe-liang Chiou 
56d616ba5fSSimon Glass struct tpm_tis_lpc_priv {
57d616ba5fSSimon Glass 	struct tpm_locality *regs;
58d616ba5fSSimon Glass };
59d616ba5fSSimon Glass 
6090899cc0SChe-liang Chiou /*
6190899cc0SChe-liang Chiou  * This pointer refers to the TPM chip, 5 of its localities are mapped as an
6290899cc0SChe-liang Chiou  * array.
6390899cc0SChe-liang Chiou  */
6490899cc0SChe-liang Chiou #define TPM_TOTAL_LOCALITIES	5
6590899cc0SChe-liang Chiou 
6690899cc0SChe-liang Chiou /* Some registers' bit field definitions */
6790899cc0SChe-liang Chiou #define TIS_STS_VALID                  (1 << 7) /* 0x80 */
6890899cc0SChe-liang Chiou #define TIS_STS_COMMAND_READY          (1 << 6) /* 0x40 */
6990899cc0SChe-liang Chiou #define TIS_STS_TPM_GO                 (1 << 5) /* 0x20 */
7090899cc0SChe-liang Chiou #define TIS_STS_DATA_AVAILABLE         (1 << 4) /* 0x10 */
7190899cc0SChe-liang Chiou #define TIS_STS_EXPECT                 (1 << 3) /* 0x08 */
7290899cc0SChe-liang Chiou #define TIS_STS_RESPONSE_RETRY         (1 << 1) /* 0x02 */
7390899cc0SChe-liang Chiou 
7490899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_REG_VALID_STS   (1 << 7) /* 0x80 */
7590899cc0SChe-liang Chiou #define TIS_ACCESS_ACTIVE_LOCALITY     (1 << 5) /* 0x20 */
7690899cc0SChe-liang Chiou #define TIS_ACCESS_BEEN_SEIZED         (1 << 4) /* 0x10 */
7790899cc0SChe-liang Chiou #define TIS_ACCESS_SEIZE               (1 << 3) /* 0x08 */
7890899cc0SChe-liang Chiou #define TIS_ACCESS_PENDING_REQUEST     (1 << 2) /* 0x04 */
7990899cc0SChe-liang Chiou #define TIS_ACCESS_REQUEST_USE         (1 << 1) /* 0x02 */
8090899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_ESTABLISHMENT   (1 << 0) /* 0x01 */
8190899cc0SChe-liang Chiou 
8290899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_MASK       (0xffff)
8390899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_SHIFT      (8)
8490899cc0SChe-liang Chiou 
8590899cc0SChe-liang Chiou  /* 1 second is plenty for anything TPM does. */
8690899cc0SChe-liang Chiou #define MAX_DELAY_US	(1000 * 1000)
8790899cc0SChe-liang Chiou 
8890899cc0SChe-liang Chiou /* Retrieve burst count value out of the status register contents. */
8990899cc0SChe-liang Chiou static u16 burst_count(u32 status)
9090899cc0SChe-liang Chiou {
91d616ba5fSSimon Glass 	return (status >> TIS_STS_BURST_COUNT_SHIFT) &
92d616ba5fSSimon Glass 			TIS_STS_BURST_COUNT_MASK;
9390899cc0SChe-liang Chiou }
9490899cc0SChe-liang Chiou 
9590899cc0SChe-liang Chiou /* TPM access wrappers to support tracing */
96d616ba5fSSimon Glass static u8 tpm_read_byte(struct tpm_tis_lpc_priv *priv, const u8 *ptr)
9790899cc0SChe-liang Chiou {
9890899cc0SChe-liang Chiou 	u8  ret = readb(ptr);
9990899cc0SChe-liang Chiou 	debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
100d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
10190899cc0SChe-liang Chiou 	return ret;
10290899cc0SChe-liang Chiou }
10390899cc0SChe-liang Chiou 
104d616ba5fSSimon Glass static u32 tpm_read_word(struct tpm_tis_lpc_priv *priv, const u32 *ptr)
10590899cc0SChe-liang Chiou {
10690899cc0SChe-liang Chiou 	u32  ret = readl(ptr);
10790899cc0SChe-liang Chiou 	debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
108d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
10990899cc0SChe-liang Chiou 	return ret;
11090899cc0SChe-liang Chiou }
11190899cc0SChe-liang Chiou 
112d616ba5fSSimon Glass static void tpm_write_byte(struct tpm_tis_lpc_priv *priv, u8 value, u8 *ptr)
11390899cc0SChe-liang Chiou {
11490899cc0SChe-liang Chiou 	debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
115d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
11690899cc0SChe-liang Chiou 	writeb(value, ptr);
11790899cc0SChe-liang Chiou }
11890899cc0SChe-liang Chiou 
119d616ba5fSSimon Glass static void tpm_write_word(struct tpm_tis_lpc_priv *priv, u32 value,
120d616ba5fSSimon Glass 			   u32 *ptr)
12190899cc0SChe-liang Chiou {
12290899cc0SChe-liang Chiou 	debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
123d616ba5fSSimon Glass 	      (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
12490899cc0SChe-liang Chiou 	writel(value, ptr);
12590899cc0SChe-liang Chiou }
12690899cc0SChe-liang Chiou 
12790899cc0SChe-liang Chiou /*
12890899cc0SChe-liang Chiou  * tis_wait_reg()
12990899cc0SChe-liang Chiou  *
13090899cc0SChe-liang Chiou  * Wait for at least a second for a register to change its state to match the
13190899cc0SChe-liang Chiou  * expected state. Normally the transition happens within microseconds.
13290899cc0SChe-liang Chiou  *
13390899cc0SChe-liang Chiou  * @reg - pointer to the TPM register
13490899cc0SChe-liang Chiou  * @mask - bitmask for the bitfield(s) to watch
13590899cc0SChe-liang Chiou  * @expected - value the field(s) are supposed to be set to
13690899cc0SChe-liang Chiou  *
13790899cc0SChe-liang Chiou  * Returns the register contents in case the expected value was found in the
138d616ba5fSSimon Glass  * appropriate register bits, or -ETIMEDOUT on timeout.
13990899cc0SChe-liang Chiou  */
140d616ba5fSSimon Glass static int tis_wait_reg(struct tpm_tis_lpc_priv *priv, u32 *reg, u8 mask,
141d616ba5fSSimon Glass 			u8 expected)
14290899cc0SChe-liang Chiou {
14390899cc0SChe-liang Chiou 	u32 time_us = MAX_DELAY_US;
14490899cc0SChe-liang Chiou 
14590899cc0SChe-liang Chiou 	while (time_us > 0) {
146d616ba5fSSimon Glass 		u32 value = tpm_read_word(priv, reg);
14790899cc0SChe-liang Chiou 		if ((value & mask) == expected)
14890899cc0SChe-liang Chiou 			return value;
14990899cc0SChe-liang Chiou 		udelay(1); /* 1 us */
15090899cc0SChe-liang Chiou 		time_us--;
15190899cc0SChe-liang Chiou 	}
152d616ba5fSSimon Glass 
153d616ba5fSSimon Glass 	return -ETIMEDOUT;
15490899cc0SChe-liang Chiou }
15590899cc0SChe-liang Chiou 
15690899cc0SChe-liang Chiou /*
15790899cc0SChe-liang Chiou  * Probe the TPM device and try determining its manufacturer/device name.
15890899cc0SChe-liang Chiou  *
159d616ba5fSSimon Glass  * Returns 0 on success, -ve on error
16090899cc0SChe-liang Chiou  */
161d616ba5fSSimon Glass static int tpm_tis_lpc_probe(struct udevice *dev)
16290899cc0SChe-liang Chiou {
163d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
164d616ba5fSSimon Glass 	fdt_addr_t addr;
165d616ba5fSSimon Glass 	u32 didvid;
166*a982b6f5SGeorge McCollister 	ulong chip_type = dev_get_driver_data(dev);
16790899cc0SChe-liang Chiou 
168d616ba5fSSimon Glass 	addr = dev_get_addr(dev);
169d616ba5fSSimon Glass 	if (addr == FDT_ADDR_T_NONE)
170d616ba5fSSimon Glass 		return -EINVAL;
171d616ba5fSSimon Glass 	priv->regs = map_sysmem(addr, 0);
172d616ba5fSSimon Glass 	didvid = tpm_read_word(priv, &priv->regs[0].did_vid);
17390899cc0SChe-liang Chiou 
174*a982b6f5SGeorge McCollister 	if (didvid != chip_didvid[chip_type]) {
175*a982b6f5SGeorge McCollister 		u32 vid, did;
17690899cc0SChe-liang Chiou 		vid = didvid & 0xffff;
17790899cc0SChe-liang Chiou 		did = (didvid >> 16) & 0xffff;
178d616ba5fSSimon Glass 		debug("Invalid vendor/device ID %04x/%04x\n", vid, did);
179*a982b6f5SGeorge McCollister 		return -ENODEV;
18090899cc0SChe-liang Chiou 	}
18190899cc0SChe-liang Chiou 
182*a982b6f5SGeorge McCollister 	debug("Found TPM: %s\n", chip_name[chip_type]);
183d616ba5fSSimon Glass 
18490899cc0SChe-liang Chiou 	return 0;
18590899cc0SChe-liang Chiou }
18690899cc0SChe-liang Chiou 
18790899cc0SChe-liang Chiou /*
18890899cc0SChe-liang Chiou  * tis_senddata()
18990899cc0SChe-liang Chiou  *
19090899cc0SChe-liang Chiou  * send the passed in data to the TPM device.
19190899cc0SChe-liang Chiou  *
19290899cc0SChe-liang Chiou  * @data - address of the data to send, byte by byte
19390899cc0SChe-liang Chiou  * @len - length of the data to send
19490899cc0SChe-liang Chiou  *
195d616ba5fSSimon Glass  * Returns 0 on success, -ve on error (in case the device does not accept
196d616ba5fSSimon Glass  * the entire command).
19790899cc0SChe-liang Chiou  */
198d616ba5fSSimon Glass static int tis_senddata(struct udevice *dev, const u8 *data, size_t len)
19990899cc0SChe-liang Chiou {
200d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
201d616ba5fSSimon Glass 	struct tpm_locality *regs = priv->regs;
20290899cc0SChe-liang Chiou 	u32 offset = 0;
20390899cc0SChe-liang Chiou 	u16 burst = 0;
20490899cc0SChe-liang Chiou 	u32 max_cycles = 0;
20590899cc0SChe-liang Chiou 	u8 locality = 0;
20690899cc0SChe-liang Chiou 	u32 value;
20790899cc0SChe-liang Chiou 
208d616ba5fSSimon Glass 	value = tis_wait_reg(priv, &regs[locality].tpm_status,
20990899cc0SChe-liang Chiou 			     TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
210d616ba5fSSimon Glass 	if (value == -ETIMEDOUT) {
21190899cc0SChe-liang Chiou 		printf("%s:%d - failed to get 'command_ready' status\n",
21290899cc0SChe-liang Chiou 		       __FILE__, __LINE__);
213d616ba5fSSimon Glass 		return value;
21490899cc0SChe-liang Chiou 	}
21590899cc0SChe-liang Chiou 	burst = burst_count(value);
21690899cc0SChe-liang Chiou 
21790899cc0SChe-liang Chiou 	while (1) {
21890899cc0SChe-liang Chiou 		unsigned count;
21990899cc0SChe-liang Chiou 
22090899cc0SChe-liang Chiou 		/* Wait till the device is ready to accept more data. */
22190899cc0SChe-liang Chiou 		while (!burst) {
22290899cc0SChe-liang Chiou 			if (max_cycles++ == MAX_DELAY_US) {
22322230e91SSimon Glass 				printf("%s:%d failed to feed %zd bytes of %zd\n",
22490899cc0SChe-liang Chiou 				       __FILE__, __LINE__, len - offset, len);
225d616ba5fSSimon Glass 				return -ETIMEDOUT;
22690899cc0SChe-liang Chiou 			}
22790899cc0SChe-liang Chiou 			udelay(1);
228d616ba5fSSimon Glass 			burst = burst_count(tpm_read_word(priv,
229d616ba5fSSimon Glass 					&regs[locality].tpm_status));
23090899cc0SChe-liang Chiou 		}
23190899cc0SChe-liang Chiou 
23290899cc0SChe-liang Chiou 		max_cycles = 0;
23390899cc0SChe-liang Chiou 
23490899cc0SChe-liang Chiou 		/*
23590899cc0SChe-liang Chiou 		 * Calculate number of bytes the TPM is ready to accept in one
23690899cc0SChe-liang Chiou 		 * shot.
23790899cc0SChe-liang Chiou 		 *
23890899cc0SChe-liang Chiou 		 * We want to send the last byte outside of the loop (hence
23990899cc0SChe-liang Chiou 		 * the -1 below) to make sure that the 'expected' status bit
24090899cc0SChe-liang Chiou 		 * changes to zero exactly after the last byte is fed into the
24190899cc0SChe-liang Chiou 		 * FIFO.
24290899cc0SChe-liang Chiou 		 */
24322230e91SSimon Glass 		count = min((size_t)burst, len - offset - 1);
24490899cc0SChe-liang Chiou 		while (count--)
245d616ba5fSSimon Glass 			tpm_write_byte(priv, data[offset++],
246d616ba5fSSimon Glass 				       &regs[locality].data);
24790899cc0SChe-liang Chiou 
248d616ba5fSSimon Glass 		value = tis_wait_reg(priv, &regs[locality].tpm_status,
24990899cc0SChe-liang Chiou 				     TIS_STS_VALID, TIS_STS_VALID);
25090899cc0SChe-liang Chiou 
251d616ba5fSSimon Glass 		if ((value == -ETIMEDOUT) || !(value & TIS_STS_EXPECT)) {
25290899cc0SChe-liang Chiou 			printf("%s:%d TPM command feed overflow\n",
25390899cc0SChe-liang Chiou 			       __FILE__, __LINE__);
254d616ba5fSSimon Glass 			return value == -ETIMEDOUT ? value : -EIO;
25590899cc0SChe-liang Chiou 		}
25690899cc0SChe-liang Chiou 
25790899cc0SChe-liang Chiou 		burst = burst_count(value);
25890899cc0SChe-liang Chiou 		if ((offset == (len - 1)) && burst) {
25990899cc0SChe-liang Chiou 			/*
26090899cc0SChe-liang Chiou 			 * We need to be able to send the last byte to the
26190899cc0SChe-liang Chiou 			 * device, so burst size must be nonzero before we
26290899cc0SChe-liang Chiou 			 * break out.
26390899cc0SChe-liang Chiou 			 */
26490899cc0SChe-liang Chiou 			break;
26590899cc0SChe-liang Chiou 		}
26690899cc0SChe-liang Chiou 	}
26790899cc0SChe-liang Chiou 
26890899cc0SChe-liang Chiou 	/* Send the last byte. */
269d616ba5fSSimon Glass 	tpm_write_byte(priv, data[offset++], &regs[locality].data);
27090899cc0SChe-liang Chiou 	/*
27190899cc0SChe-liang Chiou 	 * Verify that TPM does not expect any more data as part of this
27290899cc0SChe-liang Chiou 	 * command.
27390899cc0SChe-liang Chiou 	 */
274d616ba5fSSimon Glass 	value = tis_wait_reg(priv, &regs[locality].tpm_status,
27590899cc0SChe-liang Chiou 			     TIS_STS_VALID, TIS_STS_VALID);
276d616ba5fSSimon Glass 	if ((value == -ETIMEDOUT) || (value & TIS_STS_EXPECT)) {
27790899cc0SChe-liang Chiou 		printf("%s:%d unexpected TPM status 0x%x\n",
27890899cc0SChe-liang Chiou 		       __FILE__, __LINE__, value);
279d616ba5fSSimon Glass 		return value == -ETIMEDOUT ? value : -EIO;
28090899cc0SChe-liang Chiou 	}
28190899cc0SChe-liang Chiou 
28290899cc0SChe-liang Chiou 	/* OK, sitting pretty, let's start the command execution. */
283d616ba5fSSimon Glass 	tpm_write_word(priv, TIS_STS_TPM_GO, &regs[locality].tpm_status);
28490899cc0SChe-liang Chiou 	return 0;
28590899cc0SChe-liang Chiou }
28690899cc0SChe-liang Chiou 
28790899cc0SChe-liang Chiou /*
28890899cc0SChe-liang Chiou  * tis_readresponse()
28990899cc0SChe-liang Chiou  *
29090899cc0SChe-liang Chiou  * read the TPM device response after a command was issued.
29190899cc0SChe-liang Chiou  *
29290899cc0SChe-liang Chiou  * @buffer - address where to read the response, byte by byte.
29390899cc0SChe-liang Chiou  * @len - pointer to the size of buffer
29490899cc0SChe-liang Chiou  *
29590899cc0SChe-liang Chiou  * On success stores the number of received bytes to len and returns 0. On
29690899cc0SChe-liang Chiou  * errors (misformatted TPM data or synchronization problems) returns
297d616ba5fSSimon Glass  * -ve value.
29890899cc0SChe-liang Chiou  */
299d616ba5fSSimon Glass static int tis_readresponse(struct udevice *dev, u8 *buffer, size_t len)
30090899cc0SChe-liang Chiou {
301d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
302d616ba5fSSimon Glass 	struct tpm_locality *regs = priv->regs;
30390899cc0SChe-liang Chiou 	u16 burst;
30490899cc0SChe-liang Chiou 	u32 value;
30590899cc0SChe-liang Chiou 	u32 offset = 0;
30690899cc0SChe-liang Chiou 	u8 locality = 0;
30790899cc0SChe-liang Chiou 	const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
308d616ba5fSSimon Glass 	u32 expected_count = len;
30990899cc0SChe-liang Chiou 	int max_cycles = 0;
31090899cc0SChe-liang Chiou 
31190899cc0SChe-liang Chiou 	/* Wait for the TPM to process the command. */
312d616ba5fSSimon Glass 	value = tis_wait_reg(priv, &regs[locality].tpm_status,
31390899cc0SChe-liang Chiou 			      has_data, has_data);
314d616ba5fSSimon Glass 	if (value == -ETIMEDOUT) {
31590899cc0SChe-liang Chiou 		printf("%s:%d failed processing command\n",
31690899cc0SChe-liang Chiou 		       __FILE__, __LINE__);
317d616ba5fSSimon Glass 		return value;
31890899cc0SChe-liang Chiou 	}
31990899cc0SChe-liang Chiou 
32090899cc0SChe-liang Chiou 	do {
32190899cc0SChe-liang Chiou 		while ((burst = burst_count(value)) == 0) {
32290899cc0SChe-liang Chiou 			if (max_cycles++ == MAX_DELAY_US) {
32390899cc0SChe-liang Chiou 				printf("%s:%d TPM stuck on read\n",
32490899cc0SChe-liang Chiou 				       __FILE__, __LINE__);
325d616ba5fSSimon Glass 				return -EIO;
32690899cc0SChe-liang Chiou 			}
32790899cc0SChe-liang Chiou 			udelay(1);
328d616ba5fSSimon Glass 			value = tpm_read_word(priv, &regs[locality].tpm_status);
32990899cc0SChe-liang Chiou 		}
33090899cc0SChe-liang Chiou 
33190899cc0SChe-liang Chiou 		max_cycles = 0;
33290899cc0SChe-liang Chiou 
33390899cc0SChe-liang Chiou 		while (burst-- && (offset < expected_count)) {
334d616ba5fSSimon Glass 			buffer[offset++] = tpm_read_byte(priv,
335d616ba5fSSimon Glass 						&regs[locality].data);
33690899cc0SChe-liang Chiou 
33790899cc0SChe-liang Chiou 			if (offset == 6) {
33890899cc0SChe-liang Chiou 				/*
33990899cc0SChe-liang Chiou 				 * We got the first six bytes of the reply,
34090899cc0SChe-liang Chiou 				 * let's figure out how many bytes to expect
34190899cc0SChe-liang Chiou 				 * total - it is stored as a 4 byte number in
34290899cc0SChe-liang Chiou 				 * network order, starting with offset 2 into
34390899cc0SChe-liang Chiou 				 * the body of the reply.
34490899cc0SChe-liang Chiou 				 */
34590899cc0SChe-liang Chiou 				u32 real_length;
34690899cc0SChe-liang Chiou 				memcpy(&real_length,
34790899cc0SChe-liang Chiou 				       buffer + 2,
34890899cc0SChe-liang Chiou 				       sizeof(real_length));
34990899cc0SChe-liang Chiou 				expected_count = be32_to_cpu(real_length);
35090899cc0SChe-liang Chiou 
35190899cc0SChe-liang Chiou 				if ((expected_count < offset) ||
352d616ba5fSSimon Glass 				    (expected_count > len)) {
35390899cc0SChe-liang Chiou 					printf("%s:%d bad response size %d\n",
35490899cc0SChe-liang Chiou 					       __FILE__, __LINE__,
35590899cc0SChe-liang Chiou 					       expected_count);
356d616ba5fSSimon Glass 					return -ENOSPC;
35790899cc0SChe-liang Chiou 				}
35890899cc0SChe-liang Chiou 			}
35990899cc0SChe-liang Chiou 		}
36090899cc0SChe-liang Chiou 
36190899cc0SChe-liang Chiou 		/* Wait for the next portion. */
362d616ba5fSSimon Glass 		value = tis_wait_reg(priv, &regs[locality].tpm_status,
36390899cc0SChe-liang Chiou 				     TIS_STS_VALID, TIS_STS_VALID);
364d616ba5fSSimon Glass 		if (value == -ETIMEDOUT) {
36590899cc0SChe-liang Chiou 			printf("%s:%d failed to read response\n",
36690899cc0SChe-liang Chiou 			       __FILE__, __LINE__);
367d616ba5fSSimon Glass 			return value;
36890899cc0SChe-liang Chiou 		}
36990899cc0SChe-liang Chiou 
37090899cc0SChe-liang Chiou 		if (offset == expected_count)
37190899cc0SChe-liang Chiou 			break;	/* We got all we needed. */
37290899cc0SChe-liang Chiou 
37390899cc0SChe-liang Chiou 	} while ((value & has_data) == has_data);
37490899cc0SChe-liang Chiou 
37590899cc0SChe-liang Chiou 	/*
37690899cc0SChe-liang Chiou 	 * Make sure we indeed read all there was. The TIS_STS_VALID bit is
37790899cc0SChe-liang Chiou 	 * known to be set.
37890899cc0SChe-liang Chiou 	 */
37990899cc0SChe-liang Chiou 	if (value & TIS_STS_DATA_AVAILABLE) {
38090899cc0SChe-liang Chiou 		printf("%s:%d wrong receive status %x\n",
38190899cc0SChe-liang Chiou 		       __FILE__, __LINE__, value);
382d616ba5fSSimon Glass 		return -EBADMSG;
38390899cc0SChe-liang Chiou 	}
38490899cc0SChe-liang Chiou 
38590899cc0SChe-liang Chiou 	/* Tell the TPM that we are done. */
386d616ba5fSSimon Glass 	tpm_write_word(priv, TIS_STS_COMMAND_READY,
387d616ba5fSSimon Glass 		       &regs[locality].tpm_status);
388d616ba5fSSimon Glass 
389d616ba5fSSimon Glass 	return offset;
39090899cc0SChe-liang Chiou }
39190899cc0SChe-liang Chiou 
392d616ba5fSSimon Glass static int tpm_tis_lpc_open(struct udevice *dev)
39390899cc0SChe-liang Chiou {
394d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
395d616ba5fSSimon Glass 	struct tpm_locality *regs = priv->regs;
39690899cc0SChe-liang Chiou 	u8 locality = 0; /* we use locality zero for everything. */
397d616ba5fSSimon Glass 	int ret;
39890899cc0SChe-liang Chiou 
39990899cc0SChe-liang Chiou 	/* now request access to locality. */
400d616ba5fSSimon Glass 	tpm_write_word(priv, TIS_ACCESS_REQUEST_USE, &regs[locality].access);
40190899cc0SChe-liang Chiou 
40290899cc0SChe-liang Chiou 	/* did we get a lock? */
403d616ba5fSSimon Glass 	ret = tis_wait_reg(priv, &regs[locality].access,
40490899cc0SChe-liang Chiou 			 TIS_ACCESS_ACTIVE_LOCALITY,
405d616ba5fSSimon Glass 			 TIS_ACCESS_ACTIVE_LOCALITY);
406d616ba5fSSimon Glass 	if (ret == -ETIMEDOUT) {
40790899cc0SChe-liang Chiou 		printf("%s:%d - failed to lock locality %d\n",
40890899cc0SChe-liang Chiou 		       __FILE__, __LINE__, locality);
409d616ba5fSSimon Glass 		return ret;
41090899cc0SChe-liang Chiou 	}
41190899cc0SChe-liang Chiou 
412d616ba5fSSimon Glass 	tpm_write_word(priv, TIS_STS_COMMAND_READY,
413d616ba5fSSimon Glass 		       &regs[locality].tpm_status);
41490899cc0SChe-liang Chiou 	return 0;
41590899cc0SChe-liang Chiou }
41690899cc0SChe-liang Chiou 
417d616ba5fSSimon Glass static int tpm_tis_lpc_close(struct udevice *dev)
41890899cc0SChe-liang Chiou {
419d616ba5fSSimon Glass 	struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
420d616ba5fSSimon Glass 	struct tpm_locality *regs = priv->regs;
42190899cc0SChe-liang Chiou 	u8 locality = 0;
42290899cc0SChe-liang Chiou 
423d616ba5fSSimon Glass 	if (tpm_read_word(priv, &regs[locality].access) &
42490899cc0SChe-liang Chiou 	    TIS_ACCESS_ACTIVE_LOCALITY) {
425d616ba5fSSimon Glass 		tpm_write_word(priv, TIS_ACCESS_ACTIVE_LOCALITY,
426d616ba5fSSimon Glass 			       &regs[locality].access);
42790899cc0SChe-liang Chiou 
428d616ba5fSSimon Glass 		if (tis_wait_reg(priv, &regs[locality].access,
429d616ba5fSSimon Glass 				 TIS_ACCESS_ACTIVE_LOCALITY, 0) == -ETIMEDOUT) {
43090899cc0SChe-liang Chiou 			printf("%s:%d - failed to release locality %d\n",
43190899cc0SChe-liang Chiou 			       __FILE__, __LINE__, locality);
432d616ba5fSSimon Glass 			return -ETIMEDOUT;
43390899cc0SChe-liang Chiou 		}
43490899cc0SChe-liang Chiou 	}
43590899cc0SChe-liang Chiou 	return 0;
43690899cc0SChe-liang Chiou }
43790899cc0SChe-liang Chiou 
438d616ba5fSSimon Glass static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
43990899cc0SChe-liang Chiou {
440*a982b6f5SGeorge McCollister 	ulong chip_type = dev_get_driver_data(dev);
441*a982b6f5SGeorge McCollister 
442d616ba5fSSimon Glass 	if (size < 50)
443d616ba5fSSimon Glass 		return -ENOSPC;
444d616ba5fSSimon Glass 
445*a982b6f5SGeorge McCollister 	return snprintf(buf, size, "1.2 TPM (%s)",
446*a982b6f5SGeorge McCollister 			chip_name[chip_type]);
44790899cc0SChe-liang Chiou }
44890899cc0SChe-liang Chiou 
449d616ba5fSSimon Glass 
450d616ba5fSSimon Glass static const struct tpm_ops tpm_tis_lpc_ops = {
451d616ba5fSSimon Glass 	.open		= tpm_tis_lpc_open,
452d616ba5fSSimon Glass 	.close		= tpm_tis_lpc_close,
453d616ba5fSSimon Glass 	.get_desc	= tpm_tis_get_desc,
454d616ba5fSSimon Glass 	.send		= tis_senddata,
455d616ba5fSSimon Glass 	.recv		= tis_readresponse,
456d616ba5fSSimon Glass };
457d616ba5fSSimon Glass 
458d616ba5fSSimon Glass static const struct udevice_id tpm_tis_lpc_ids[] = {
459*a982b6f5SGeorge McCollister 	{ .compatible = "infineon,slb9635lpc", .data = SLB9635 },
460*a982b6f5SGeorge McCollister 	{ .compatible = "atmel,at97sc3204", .data = AT97SC3204 },
461d616ba5fSSimon Glass 	{ }
462d616ba5fSSimon Glass };
463d616ba5fSSimon Glass 
464d616ba5fSSimon Glass U_BOOT_DRIVER(tpm_tis_lpc) = {
465d616ba5fSSimon Glass 	.name   = "tpm_tis_lpc",
466d616ba5fSSimon Glass 	.id     = UCLASS_TPM,
467d616ba5fSSimon Glass 	.of_match = tpm_tis_lpc_ids,
468d616ba5fSSimon Glass 	.ops    = &tpm_tis_lpc_ops,
469d616ba5fSSimon Glass 	.probe	= tpm_tis_lpc_probe,
470d616ba5fSSimon Glass 	.priv_auto_alloc_size = sizeof(struct tpm_tis_lpc_priv),
471d616ba5fSSimon Glass };
472